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