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.smtp;
019    
020    import java.io.IOException;
021    import java.net.Socket;
022    import javax.net.ssl.KeyManager;
023    import javax.net.ssl.SSLContext;
024    import javax.net.ssl.SSLException;
025    import javax.net.ssl.SSLSocket;
026    import javax.net.ssl.SSLSocketFactory;
027    import javax.net.ssl.TrustManager;
028    
029    import org.apache.commons.net.util.SSLContextUtils;
030    
031    /**
032     * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP.
033     * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right
034     * after the connection has been established. In explicit mode (the default), SSL/TLS
035     * negotiation starts when the user calls execTLS() and the server accepts the command.
036     * Implicit usage:
037     *               SMTPSClient c = new SMTPSClient(true);
038     *               c.connect("127.0.0.1", 465);
039     * Explicit usage:
040     *               SMTPSClient c = new SMTPSClient();
041     *               c.connect("127.0.0.1", 25);
042     *               if (c.execTLS()) { /rest of the commands here/ }
043     * @since 3.0
044     */
045    public class SMTPSClient extends SMTPClient
046    {
047        /** Default secure socket protocol name, like TLS */
048        private static final String DEFAULT_PROTOCOL = "TLS";
049    
050        /** The security mode. True - Implicit Mode / False - Explicit Mode. */
051        private final boolean isImplicit;
052        /** The secure socket protocol to be used, like SSL/TLS. */
053        private final String protocol;
054        /** The context object. */
055        private SSLContext context = null;
056        /** The cipher suites. SSLSockets have a default set of these anyway,
057            so no initialization required. */
058        private String[] suites = null;
059        /** The protocol versions. */
060        private String[] protocols = null;
061    
062        /** The {@link TrustManager} implementation, default null (i.e. use system managers). */
063        private TrustManager trustManager = null;
064    
065        /** The {@link KeyManager}, default null (i.e. use system managers). */
066        private KeyManager keyManager = null; // seems not to be required
067    
068        /**
069         * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
070         * Sets security mode to explicit (isImplicit = false).
071         */
072        public SMTPSClient()
073        {
074            this(DEFAULT_PROTOCOL, false);
075        }
076    
077        /**
078         * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
079         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
080         */
081        public SMTPSClient(boolean implicit)
082        {
083            this(DEFAULT_PROTOCOL, implicit);
084        }
085    
086        /**
087         * Constructor for SMTPSClient, using explicit security mode.
088         * @param proto the protocol.
089         */
090        public SMTPSClient(String proto)
091        {
092            this(proto, false);
093        }
094    
095        /**
096         * Constructor for SMTPSClient.
097         * @param proto the protocol.
098         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
099         */
100        public SMTPSClient(String proto, boolean implicit)
101        {
102            protocol = proto;
103            isImplicit = implicit;
104        }
105    
106        /**
107         * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
108         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
109         * @param ctx A pre-configured SSL Context.
110         */
111        public SMTPSClient(boolean implicit, SSLContext ctx)
112        {
113            isImplicit = implicit;
114            context = ctx;
115            protocol = DEFAULT_PROTOCOL;
116        }
117    
118        /**
119         * Constructor for SMTPSClient.
120         * @param context A pre-configured SSL Context.
121         * @see #SMTPSClient(boolean, SSLContext)
122         */
123        public SMTPSClient(SSLContext context)
124        {
125            this(false, context);
126        }
127    
128        /**
129         * Because there are so many connect() methods,
130         * the _connectAction_() method is provided as a means of performing
131         * some action immediately after establishing a connection,
132         * rather than reimplementing all of the connect() methods.
133         * @throws IOException If it is thrown by _connectAction_().
134         * @see org.apache.commons.net.SocketClient#_connectAction_()
135         */
136        @Override
137        protected void _connectAction_() throws IOException
138        {
139            // Implicit mode.
140            if (isImplicit) performSSLNegotiation();
141            super._connectAction_();
142            // Explicit mode - don't do anything. The user calls execTLS()
143        }
144    
145        /**
146         * Performs a lazy init of the SSL context.
147         * @throws IOException When could not initialize the SSL context.
148         */
149        private void initSSLContext() throws IOException
150        {
151            if (context == null)
152            {
153                context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
154            }
155        }
156    
157        /**
158         * SSL/TLS negotiation. Acquires an SSL socket of a
159         * connection and carries out handshake processing.
160         * @throws IOException If server negotiation fails.
161         */
162        private void performSSLNegotiation() throws IOException
163        {
164            initSSLContext();
165    
166            SSLSocketFactory ssf = context.getSocketFactory();
167            String ip = getRemoteAddress().getHostAddress();
168            int port = getRemotePort();
169            SSLSocket socket =
170                (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
171            socket.setEnableSessionCreation(true);
172            socket.setUseClientMode(true);
173    
174            if (protocols != null) socket.setEnabledProtocols(protocols);
175            if (suites != null) socket.setEnabledCipherSuites(suites);
176            socket.startHandshake();
177    
178            _socket_ = socket;
179            _input_ = socket.getInputStream();
180            _output_ = socket.getOutputStream();
181        }
182    
183        /**
184         * Get the {@link KeyManager} instance.
185         * @return The current {@link KeyManager} instance.
186         */
187        public KeyManager getKeyManager()
188        {
189            return keyManager;
190        }
191    
192        /**
193         * Set a {@link KeyManager} to use.
194         * @param newKeyManager The KeyManager implementation to set.
195         * @see org.apache.commons.net.util.KeyManagerUtils
196         */
197        public void setKeyManager(KeyManager newKeyManager)
198        {
199            keyManager = newKeyManager;
200        }
201    
202        /**
203         * Controls which particular cipher suites are enabled for use on this
204         * connection. Called before server negotiation.
205         * @param cipherSuites The cipher suites.
206         */
207        public void setEnabledCipherSuites(String[] cipherSuites)
208        {
209            suites = new String[cipherSuites.length];
210            System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
211        }
212    
213        /**
214         * Returns the names of the cipher suites which could be enabled
215         * for use on this connection.
216         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
217         * @return An array of cipher suite names, or <code>null</code>.
218         */
219        public String[] getEnabledCipherSuites()
220        {
221            if (_socket_ instanceof SSLSocket)
222            {
223                return ((SSLSocket)_socket_).getEnabledCipherSuites();
224            }
225            return null;
226        }
227    
228        /**
229         * Controls which particular protocol versions are enabled for use on this
230         * connection. I perform setting before a server negotiation.
231         * @param protocolVersions The protocol versions.
232         */
233        public void setEnabledProtocols(String[] protocolVersions)
234        {
235            protocols = new String[protocolVersions.length];
236            System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
237        }
238    
239        /**
240         * Returns the names of the protocol versions which are currently
241         * enabled for use on this connection.
242         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
243         * @return An array of protocols, or <code>null</code>.
244         */
245        public String[] getEnabledProtocols()
246        {
247            if (_socket_ instanceof SSLSocket)
248            {
249                return ((SSLSocket)_socket_).getEnabledProtocols();
250            }
251            return null;
252        }
253    
254        /**
255         * The TLS command execution.
256         * @throws IOException If an I/O error occurs while sending
257         * the command or performing the negotiation.
258         * @return TRUE if the command and negotiation succeeded.
259         */
260        public boolean execTLS() throws SSLException, IOException
261        {
262            if (!SMTPReply.isPositiveCompletion(sendCommand("STARTTLS")))
263            {
264                return false;
265                //throw new SSLException(getReplyString());
266            }
267            performSSLNegotiation();
268            return true;
269        }
270    
271        /**
272         * Get the currently configured {@link TrustManager}.
273         * @return A TrustManager instance.
274         */
275        public TrustManager getTrustManager()
276        {
277            return trustManager;
278        }
279    
280        /**
281         * Override the default {@link TrustManager} to use.
282         * @param newTrustManager The TrustManager implementation to set.
283         * @see org.apache.commons.net.util.TrustManagerUtils
284         */
285        public void setTrustManager(TrustManager newTrustManager)
286        {
287            trustManager = newTrustManager;
288        }
289    }
290    
291    /* kate: indent-width 4; replace-tabs on; */