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 }