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    }