1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.smtp;
19
20 import java.io.BufferedWriter;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.OutputStreamWriter;
24
25 import javax.net.ssl.KeyManager;
26 import javax.net.ssl.SSLContext;
27 import javax.net.ssl.SSLException;
28 import javax.net.ssl.SSLSocket;
29 import javax.net.ssl.SSLSocketFactory;
30 import javax.net.ssl.TrustManager;
31
32 import org.apache.commons.net.io.CRLFLineReader;
33 import org.apache.commons.net.util.SSLContextUtils;
34
35 /**
36 * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP.
37 * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right
38 * after the connection has been established. In explicit mode (the default), SSL/TLS
39 * negotiation starts when the user calls execTLS() and the server accepts the command.
40 * Implicit usage:
41 * SMTPSClient c = new SMTPSClient(true);
42 * c.connect("127.0.0.1", 465);
43 * Explicit usage:
44 * SMTPSClient c = new SMTPSClient();
45 * c.connect("127.0.0.1", 25);
46 * if (c.execTLS()) { /rest of the commands here/ }
47 * @since 3.0
48 */
49 public class SMTPSClient extends SMTPClient
50 {
51 /** Default secure socket protocol name, like TLS */
52 private static final String DEFAULT_PROTOCOL = "TLS";
53
54 /** The security mode. True - Implicit Mode / False - Explicit Mode. */
55 private final boolean isImplicit;
56 /** The secure socket protocol to be used, like SSL/TLS. */
57 private final String protocol;
58 /** The context object. */
59 private SSLContext context = null;
60 /** The cipher suites. SSLSockets have a default set of these anyway,
61 so no initialization required. */
62 private String[] suites = null;
63 /** The protocol versions. */
64 private String[] protocols = null;
65
66 /** The {@link TrustManager} implementation, default null (i.e. use system managers). */
67 private TrustManager trustManager = null;
68
69 /** The {@link KeyManager}, default null (i.e. use system managers). */
70 private KeyManager keyManager = null; // seems not to be required
71
72 /**
73 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
74 * Sets security mode to explicit (isImplicit = false).
75 */
76 public SMTPSClient()
77 {
78 this(DEFAULT_PROTOCOL, false);
79 }
80
81 /**
82 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
83 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
84 */
85 public SMTPSClient(boolean implicit)
86 {
87 this(DEFAULT_PROTOCOL, implicit);
88 }
89
90 /**
91 * Constructor for SMTPSClient, using explicit security mode.
92 * @param proto the protocol.
93 */
94 public SMTPSClient(String proto)
95 {
96 this(proto, false);
97 }
98
99 /**
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; */