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    }