001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.net.ftp;
019
020 import java.io.BufferedReader;
021 import java.io.BufferedWriter;
022 import java.io.IOException;
023 import java.io.InputStreamReader;
024 import java.io.OutputStreamWriter;
025 import java.net.Socket;
026 import java.security.KeyManagementException;
027 import java.security.NoSuchAlgorithmException;
028
029 import javax.net.ssl.KeyManager;
030 import javax.net.ssl.SSLContext;
031 import javax.net.ssl.SSLException;
032 import javax.net.ssl.SSLSocket;
033 import javax.net.ssl.SSLSocketFactory;
034 import javax.net.ssl.TrustManager;
035
036 /**
037 * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
038 * see wire-level SSL details.
039 *
040 * @version $Id: FTPSClient.java 999437 2010-09-21 14:37:22Z sebb $
041 * @since 2.0
042 */
043 public class FTPSClient extends FTPClient {
044
045 /** @deprecated - not used - will be removed in next major release */
046 @Deprecated
047 public static String KEYSTORE_ALGORITHM;
048
049 /** @deprecated - not used - will be removed in next major release */
050 @Deprecated
051 public static String TRUSTSTORE_ALGORITHM;
052
053 /** @deprecated - not used - will be removed in next major release */
054 @Deprecated
055 public static String PROVIDER;
056
057 /** @deprecated - not used - will be removed in next major release */
058 @Deprecated
059 public static String STORE_TYPE;
060
061 /** The value that I can set in PROT command (C = Clear, P = Protected) */
062 private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"};
063 /** Default PROT Command */
064 private static final String DEFAULT_PROT = "C";
065 /** Default secure socket protocol name, i.e. TLS */
066 private static final String DEFAULT_PROTOCOL = "TLS";
067
068 /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
069 private final boolean isImplicit;
070 /** The secure socket protocol to be used, e.g. SSL/TLS. */
071 private final String protocol;
072 /** The AUTH Command value */
073 private String auth = DEFAULT_PROTOCOL;
074 /** The context object. */
075 private SSLContext context;
076 /** The socket object. */
077 private Socket plainSocket;
078 /** The established socket flag. */
079 private boolean isCreation = true;
080 /** The use client mode flag. */
081 private boolean isClientMode = true;
082 /** The need client auth flag. */
083 private boolean isNeedClientAuth = false;
084 /** The want client auth flag. */
085 private boolean isWantClientAuth = false;
086 /** The cipher suites */
087 private String[] suites = null;
088 /** The protocol versions */
089 private String[] protocols = null;
090
091 /** The FTPS {@link TrustManager} implementation. */
092 private TrustManager trustManager = new FTPSTrustManager();
093
094 /** The {@link KeyManager} */
095 private KeyManager keyManager;
096
097 /**
098 * Constructor for FTPSClient.
099 * Sets security mode to explicit (isImplicit = false)
100 * @throws NoSuchAlgorithmException A requested cryptographic algorithm
101 * is not available in the environment.
102 */
103 public FTPSClient() throws NoSuchAlgorithmException {
104 this.protocol = DEFAULT_PROTOCOL;
105 this.isImplicit = false;
106 }
107
108 /**
109 * Constructor for FTPSClient.
110 * @param isImplicit The security mode (Implicit/Explicit).
111 * @throws NoSuchAlgorithmException A requested cryptographic algorithm
112 * is not available in the environment.
113 */
114 public FTPSClient(boolean isImplicit) throws NoSuchAlgorithmException {
115 this.protocol = DEFAULT_PROTOCOL;
116 this.isImplicit = isImplicit;
117 }
118
119 /**
120 * Constructor for FTPSClient.
121 * @param protocol the protocol
122 * @throws NoSuchAlgorithmException A requested cryptographic algorithm
123 * is not available in the environment.
124 */
125 public FTPSClient(String protocol) throws NoSuchAlgorithmException {
126 this.protocol = protocol;
127 this.isImplicit = false;
128 }
129
130 /**
131 * Constructor for FTPSClient.
132 * @param protocol the protocol
133 * @param isImplicit The security mode(Implicit/Explicit).
134 * @throws NoSuchAlgorithmException A requested cryptographic algorithm
135 * is not available in the environment.
136 */
137 public FTPSClient(String protocol, boolean isImplicit)
138 throws NoSuchAlgorithmException {
139 this.protocol = protocol;
140 this.isImplicit = isImplicit;
141 }
142
143 /**
144 * Constructor for FTPSClient.
145 * @param isImplicit The security mode(Implicit/Explicit).
146 * @param context A pre-configured SSL Context
147 */
148 public FTPSClient(boolean isImplicit, SSLContext context) {
149 this.isImplicit = isImplicit;
150 this.context = context;
151 this.protocol = DEFAULT_PROTOCOL;
152 }
153
154 /**
155 * Constructor for FTPSClient.
156 * @param context A pre-configured SSL Context
157 */
158 public FTPSClient(SSLContext context) {
159 this(false, context);
160 }
161
162
163 /**
164 * Set AUTH command use value.
165 * This processing is done before connected processing.
166 * @param auth AUTH command use value.
167 */
168 public void setAuthValue(String auth) {
169 this.auth = auth;
170 }
171
172 /**
173 * Return AUTH command use value.
174 * @return AUTH command use value.
175 */
176 public String getAuthValue() {
177 return this.auth;
178 }
179
180
181 /**
182 * Because there are so many connect() methods,
183 * the _connectAction_() method is provided as a means of performing
184 * some action immediately after establishing a connection,
185 * rather than reimplementing all of the connect() methods.
186 * @throws IOException If it throw by _connectAction_.
187 * @see org.apache.commons.net.SocketClient#_connectAction_()
188 */
189 @Override
190 protected void _connectAction_() throws IOException {
191 // Implicit mode.
192 if (isImplicit) sslNegotiation();
193 super._connectAction_();
194 // Explicit mode.
195 if (!isImplicit) {
196 execAUTH();
197 sslNegotiation();
198 }
199 }
200
201 /**
202 * AUTH command.
203 * @throws SSLException If it server reply code not equal "234" and "334".
204 * @throws IOException If an I/O error occurs while either sending
205 * the command.
206 */
207 private void execAUTH() throws SSLException, IOException {
208 int replyCode = sendCommand(
209 FTPSCommand._commands[FTPSCommand.AUTH], auth);
210 if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
211 // replyCode = 334
212 // I carry out an ADAT command.
213 } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
214 throw new SSLException(getReplyString());
215 }
216 }
217
218 /**
219 * Performs a lazy init of the SSL context
220 * @throws IOException
221 */
222 private void initSslContext() throws IOException {
223 if(context == null) {
224 try {
225 context = SSLContext.getInstance(protocol);
226 context.init(new KeyManager[] { getKeyManager() } , new TrustManager[] { getTrustManager() } , null);
227 } catch (KeyManagementException e) {
228 IOException ioe = new IOException("Could not initialize SSL context");
229 ioe.initCause(e);
230 throw ioe;
231 } catch (NoSuchAlgorithmException e) {
232 IOException ioe = new IOException("Could not initialize SSL context");
233 ioe.initCause(e);
234 throw ioe;
235 }
236 }
237 }
238
239 /**
240 * SSL/TLS negotiation. Acquires an SSL socket of a control
241 * connection and carries out handshake processing.
242 * @throws IOException If server negotiation fails
243 */
244 private void sslNegotiation() throws IOException {
245 plainSocket = _socket_;
246 initSslContext();
247
248 SSLSocketFactory ssf = context.getSocketFactory();
249 String ip = _socket_.getInetAddress().getHostAddress();
250 int port = _socket_.getPort();
251 SSLSocket socket =
252 (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
253 socket.setEnableSessionCreation(isCreation);
254 socket.setUseClientMode(isClientMode);
255 // server mode
256 if (!isClientMode) {
257 socket.setNeedClientAuth(isNeedClientAuth);
258 socket.setWantClientAuth(isWantClientAuth);
259 }
260
261 if (protocols != null) socket.setEnabledProtocols(protocols);
262 if (suites != null) socket.setEnabledCipherSuites(suites);
263 socket.startHandshake();
264
265 _socket_ = socket;
266 _controlInput_ = new BufferedReader(new InputStreamReader(
267 socket .getInputStream(), getControlEncoding()));
268 _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
269 socket.getOutputStream(), getControlEncoding()));
270 }
271
272 /**
273 * Get the {@link KeyManager} instance.
274 * @return The {@link KeyManager} instance
275 */
276 private KeyManager getKeyManager() {
277 return keyManager;
278 }
279
280 /**
281 * Set a {@link KeyManager} to use
282 *
283 * @param keyManager The KeyManager implementation to set.
284 */
285 public void setKeyManager(KeyManager keyManager) {
286 this.keyManager = keyManager;
287 }
288
289 /**
290 * Controls whether new a SSL session may be established by this socket.
291 * @param isCreation The established socket flag.
292 */
293 public void setEnabledSessionCreation(boolean isCreation) {
294 this.isCreation = isCreation;
295 }
296
297 /**
298 * Returns true if new SSL sessions may be established by this socket.
299 * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
300 * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
301 * this returns False.
302 * @return true - Indicates that sessions may be created;
303 * this is the default.
304 * false - indicates that an existing session must be resumed.
305 */
306 public boolean getEnableSessionCreation() {
307 if (_socket_ instanceof SSLSocket)
308 return ((SSLSocket)_socket_).getEnableSessionCreation();
309 return false;
310 }
311
312 /**
313 * Configures the socket to require client authentication.
314 * @param isNeedClientAuth The need client auth flag.
315 */
316 public void setNeedClientAuth(boolean isNeedClientAuth) {
317 this.isNeedClientAuth = isNeedClientAuth;
318 }
319
320 /**
321 * Returns true if the socket will require client authentication.
322 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
323 * @return true - If the server mode socket should request
324 * that the client authenticate itself.
325 */
326 public boolean getNeedClientAuth() {
327 if (_socket_ instanceof SSLSocket)
328 return ((SSLSocket)_socket_).getNeedClientAuth();
329 return false;
330 }
331
332 /**
333 * Configures the socket to request client authentication,
334 * but only if such a request is appropriate to the cipher
335 * suite negotiated.
336 * @param isWantClientAuth The want client auth flag.
337 */
338 public void setWantClientAuth(boolean isWantClientAuth) {
339 this.isWantClientAuth = isWantClientAuth;
340 }
341
342 /**
343 * Returns true if the socket will request client authentication.
344 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
345 * @return true - If the server mode socket should request
346 * that the client authenticate itself.
347 */
348 public boolean getWantClientAuth() {
349 if (_socket_ instanceof SSLSocket)
350 return ((SSLSocket)_socket_).getWantClientAuth();
351 return false;
352 }
353
354 /**
355 * Configures the socket to use client (or server) mode in its first
356 * handshake.
357 * @param isClientMode The use client mode flag.
358 */
359 public void setUseClientMode(boolean isClientMode) {
360 this.isClientMode = isClientMode;
361 }
362
363 /**
364 * Returns true if the socket is set to use client mode
365 * in its first handshake.
366 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
367 * @return true - If the socket should start its first handshake
368 * in "client" mode.
369 */
370 public boolean getUseClientMode() {
371 if (_socket_ instanceof SSLSocket)
372 return ((SSLSocket)_socket_).getUseClientMode();
373 return false;
374 }
375
376 /**
377 * Controls which particular cipher suites are enabled for use on this
378 * connection. Called before server negotiation.
379 * @param cipherSuites The cipher suites.
380 */
381 public void setEnabledCipherSuites(String[] cipherSuites) {
382 suites = new String[cipherSuites.length];
383 System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
384 }
385
386 /**
387 * Returns the names of the cipher suites which could be enabled
388 * for use on this connection.
389 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
390 * @return An array of cipher suite names, or <code>null</code>
391 */
392 public String[] getEnabledCipherSuites() {
393 if (_socket_ instanceof SSLSocket)
394 return ((SSLSocket)_socket_).getEnabledCipherSuites();
395 return null;
396 }
397
398 /**
399 * Controls which particular protocol versions are enabled for use on this
400 * connection. I perform setting before a server negotiation.
401 * @param protocolVersions The protocol versions.
402 */
403 public void setEnabledProtocols(String[] protocolVersions) {
404 protocols = new String[protocolVersions.length];
405 System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
406 }
407
408 /**
409 * Returns the names of the protocol versions which are currently
410 * enabled for use on this connection.
411 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
412 * @return An array of protocols, or <code>null</code>
413 */
414 public String[] getEnabledProtocols() {
415 if (_socket_ instanceof SSLSocket)
416 return ((SSLSocket)_socket_).getEnabledProtocols();
417 return null;
418 }
419
420 /**
421 * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
422 * @param pbsz Protection Buffer Size.
423 * @throws SSLException If the server reply code does not equal "200".
424 * @throws IOException If an I/O error occurs while sending
425 * the command.
426 */
427 public void execPBSZ(long pbsz) throws SSLException, IOException {
428 if (pbsz < 0 || 4294967295L < pbsz)
429 throw new IllegalArgumentException();
430 if (FTPReply.COMMAND_OK != sendCommand(
431 FTPSCommand._commands[FTPSCommand.PBSZ],String.valueOf(pbsz)))
432 throw new SSLException(getReplyString());
433 }
434
435 /**
436 * PROT command.</br>
437 * C - Clear</br>
438 * S - Safe(SSL protocol only)</br>
439 * E - Confidential(SSL protocol only)</br>
440 * P - Private
441 * @param prot Data Channel Protection Level.
442 * @throws SSLException If the server reply code does not equal "200".
443 * @throws IOException If an I/O error occurs while sending
444 * the command.
445 */
446 public void execPROT(String prot) throws SSLException, IOException {
447 if (prot == null) prot = DEFAULT_PROT;
448 if (!checkPROTValue(prot)) throw new IllegalArgumentException();
449 if (FTPReply.COMMAND_OK != sendCommand(
450 FTPSCommand._commands[FTPSCommand.PROT], prot))
451 throw new SSLException(getReplyString());
452 if (DEFAULT_PROT.equals(prot)) {
453 setSocketFactory(null);
454 setServerSocketFactory(null);
455 } else {
456 setSocketFactory(new FTPSSocketFactory(context));
457 setServerSocketFactory(new FTPSServerSocketFactory(context));
458 initSslContext();
459 }
460 }
461
462 /**
463 * Check the value that can be set in PROT Command value.
464 * @param prot Data Channel Protection Level.
465 * @return True - A set point is right / False - A set point is not right
466 */
467 private boolean checkPROTValue(String prot) {
468 for (int p = 0; p < PROT_COMMAND_VALUE.length; p++) {
469 if (PROT_COMMAND_VALUE[p].equals(prot)) return true;
470 }
471 return false;
472 }
473
474 /**
475 * Send an FTP command.
476 * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be assigned
477 * to a plain {@link Socket} instances
478 * @param command The FTP command.
479 * @return server reply.
480 * @throws IOException If an I/O error occurs while sending
481 * the command.
482 * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
483 */
484 @Override
485 public int sendCommand(String command, String args) throws IOException {
486 int repCode = super.sendCommand(command, args);
487 /* If CCC is issued, restore socket i/o streams to unsecured versions */
488 if (FTPSCommand._commands[FTPSCommand.CCC].equals(command)) {
489 if (FTPReply.COMMAND_OK == repCode) {
490 _socket_ = plainSocket;
491 _controlInput_ = new BufferedReader(
492 new InputStreamReader(
493 _socket_ .getInputStream(), getControlEncoding()));
494 _controlOutput_ = new BufferedWriter(
495 new OutputStreamWriter(
496 _socket_.getOutputStream(), getControlEncoding()));
497 setSocketFactory(null);
498 } else {
499 throw new SSLException(getReplyString());
500 }
501 }
502 return repCode;
503 }
504
505 /**
506 * Returns a socket of the data connection.
507 * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
508 * @param command The textual representation of the FTP command to send.
509 * @param arg The arguments to the FTP command.
510 * If this parameter is set to null, then the command is sent with
511 * no arguments.
512 * @return corresponding to the established data connection.
513 * Null is returned if an FTP protocol error is reported at any point
514 * during the establishment and initialization of the connection.
515 * @throws IOException If there is any problem with the connection.
516 * @see FTPClient#_openDataConnection_(int, String)
517 */
518 @Override
519 protected Socket _openDataConnection_(int command, String arg)
520 throws IOException {
521 Socket socket = super._openDataConnection_(command, arg);
522 if (socket != null && socket instanceof SSLSocket) {
523 SSLSocket sslSocket = (SSLSocket)socket;
524
525 sslSocket.setUseClientMode(isClientMode);
526 sslSocket.setEnableSessionCreation(isCreation);
527
528 // server mode
529 if (!isClientMode) {
530 sslSocket.setNeedClientAuth(isNeedClientAuth);
531 sslSocket.setWantClientAuth(isWantClientAuth);
532 }
533 if (suites != null)
534 sslSocket.setEnabledCipherSuites(suites);
535 if (protocols != null)
536 sslSocket.setEnabledProtocols(protocols);
537 sslSocket.startHandshake();
538 }
539
540 return socket;
541 }
542
543 /**
544 * Get the currently configured {@link TrustManager}.
545 *
546 * @return A TrustManager instance.
547 */
548 public TrustManager getTrustManager() {
549 return trustManager;
550 }
551
552 /**
553 * Override the default {@link TrustManager} to use.
554 *
555 * @param trustManager The TrustManager implementation to set.
556 */
557 public void setTrustManager(TrustManager trustManager) {
558 this.trustManager = trustManager;
559 }
560 }