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