    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; */