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 * https://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.pop3; 019 020import java.io.BufferedWriter; 021import java.io.IOException; 022import java.io.InputStreamReader; 023import java.io.OutputStreamWriter; 024 025import javax.net.ssl.HostnameVerifier; 026import javax.net.ssl.KeyManager; 027import javax.net.ssl.SSLContext; 028import javax.net.ssl.SSLException; 029import javax.net.ssl.SSLHandshakeException; 030import javax.net.ssl.SSLSocket; 031import javax.net.ssl.SSLSocketFactory; 032import javax.net.ssl.TrustManager; 033 034import org.apache.commons.net.io.CRLFLineReader; 035import org.apache.commons.net.util.SSLContextUtils; 036import org.apache.commons.net.util.SSLSocketUtils; 037 038/** 039 * POP3 over SSL processing. Copied from FTPSClient.java and modified to suit POP3. If implicit mode is selected (NOT the default), SSL/TLS negotiation starts 040 * right after the connection has been established. In explicit mode (the default), SSL/TLS negotiation starts when the user calls execTLS() and the server 041 * accepts the command. Implicit usage: POP3SClient c = new POP3SClient(true); c.connect("127.0.0.1", 995); Explicit usage: POP3SClient c = new POP3SClient(); 042 * c.connect("127.0.0.1", 110); if (c.execTLS()) { /rest of the commands here/ } 043 * 044 * Warning: the hostname is not verified against the certificate by default, use {@link #setHostnameVerifier(HostnameVerifier)} or 045 * {@link #setEndpointCheckingEnabled(boolean)} (on Java 1.7+) to enable verification. 046 * 047 * @since 3.0 048 */ 049public class POP3SClient extends POP3Client { 050 // from http://www.iana.org/assignments/port-numbers 051 052 // pop3s 995/tcp pop3 protocol over TLS/SSL (was spop3) 053 // pop3s 995/udp pop3 protocol over TLS/SSL (was spop3) 054 055 private static final int DEFAULT_POP3S_PORT = 995; 056 057 /** Default secure socket protocol name, like TLS */ 058 private static final String DEFAULT_PROTOCOL = "TLS"; 059 060 /** The security mode. True - Implicit Mode / False - Explicit Mode. */ 061 private final boolean isImplicit; 062 063 /** The secure socket protocol to be used, like SSL/TLS. */ 064 private final String protocol; 065 066 /** The context object. */ 067 private SSLContext context; 068 069 /** 070 * The cipher suites. SSLSockets have a default set of these anyway, so no initialization required. 071 */ 072 private String[] suites; 073 074 /** The protocol versions. */ 075 private String[] protocols // null; 076 ; // {"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"}; 077 078 /** The FTPS {@link TrustManager} implementation, default null. */ 079 private TrustManager trustManager; 080 081 /** The {@link KeyManager}, default null. */ 082 private KeyManager keyManager; 083 084 /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ 085 private HostnameVerifier hostnameVerifier; 086 087 /** Use Java 1.7+ HTTPS Endpoint Identification Algorithm. */ 088 private boolean tlsEndpointChecking; 089 090 /** 091 * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS Sets security mode to explicit. 092 */ 093 public POP3SClient() { 094 this(DEFAULT_PROTOCOL, false); 095 } 096 097 /** 098 * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 099 * 100 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 101 */ 102 public POP3SClient(final boolean implicit) { 103 this(DEFAULT_PROTOCOL, implicit); 104 } 105 106 /** 107 * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 108 * 109 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 110 * @param ctx A pre-configured SSL Context. 111 */ 112 public POP3SClient(final boolean implicit, final SSLContext ctx) { 113 this(DEFAULT_PROTOCOL, implicit, ctx); 114 } 115 116 /** 117 * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} - TLS - and isImplicit = false 118 * 119 * @param context A pre-configured SSL Context. 120 * @see #POP3SClient(boolean, SSLContext) 121 */ 122 public POP3SClient(final SSLContext context) { 123 this(false, context); 124 } 125 126 /** 127 * Constructor for POP3SClient. Sets security mode to explicit. 128 * 129 * @param proto the protocol. 130 */ 131 public POP3SClient(final String proto) { 132 this(proto, false); 133 } 134 135 /** 136 * Constructor for POP3SClient. 137 * 138 * @param proto the protocol. 139 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 140 */ 141 public POP3SClient(final String proto, final boolean implicit) { 142 this(proto, implicit, null); 143 } 144 145 /** 146 * Constructor for POP3SClient. Sets the default port to {@link #DEFAULT_POP3S_PORT} - 995 - if using implicit mode 147 * 148 * @param proto the protocol. 149 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 150 * @param ctx the context to be used 151 */ 152 public POP3SClient(final String proto, final boolean implicit, final SSLContext ctx) { 153 protocol = proto; 154 isImplicit = implicit; 155 context = ctx; 156 if (isImplicit) { 157 setDefaultPort(DEFAULT_POP3S_PORT); 158 } 159 } 160 161 /** 162 * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing 163 * a connection, rather than reimplementing all the connect() methods. 164 * 165 * @throws IOException If it is thrown by _connectAction_(). 166 * @see org.apache.commons.net.SocketClient#_connectAction_() 167 */ 168 @Override 169 protected void _connectAction_() throws IOException { 170 // Implicit mode. 171 if (isImplicit) { 172 applySocketAttributes(); 173 performSSLNegotiation(); 174 } 175 super._connectAction_(); 176 // Explicit mode - don't do anything. The user calls execTLS() 177 } 178 179 /** 180 * The TLS command execution. 181 * 182 * @throws SSLException If the server reply code is not positive. 183 * @throws IOException If an I/O error occurs while sending the command or performing the negotiation. 184 * @return TRUE if the command and negotiation succeeded. 185 */ 186 public boolean execTLS() throws SSLException, IOException { 187 if (sendCommand("STLS") != POP3Reply.OK) { 188 return false; 189 // throw new SSLException(getReplyString()); 190 } 191 performSSLNegotiation(); 192 return true; 193 } 194 195 /** 196 * Gets the names of the cipher suites which could be enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is not an 197 * {@link SSLSocket} instance, returns null. 198 * 199 * @return An array of cipher suite names, or {@code null}. 200 */ 201 public String[] getEnabledCipherSuites() { 202 if (_socket_ instanceof SSLSocket) { 203 return ((SSLSocket) _socket_).getEnabledCipherSuites(); 204 } 205 return null; 206 } 207 208 /** 209 * Gets the names of the protocol versions which are currently enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is 210 * not an {@link SSLSocket} instance, returns null. 211 * 212 * @return An array of protocols, or {@code null}. 213 */ 214 public String[] getEnabledProtocols() { 215 if (_socket_ instanceof SSLSocket) { 216 return ((SSLSocket) _socket_).getEnabledProtocols(); 217 } 218 return null; 219 } 220 221 /** 222 * Gets the currently configured {@link HostnameVerifier}. 223 * 224 * @return A HostnameVerifier instance. 225 * @since 3.4 226 */ 227 public HostnameVerifier getHostnameVerifier() { 228 return hostnameVerifier; 229 } 230 231 /** 232 * Gets the {@link KeyManager} instance. 233 * 234 * @return The current {@link KeyManager} instance. 235 */ 236 private KeyManager getKeyManager() { 237 return keyManager; 238 } 239 240 /** 241 * Gets the currently configured {@link TrustManager}. 242 * 243 * @return A TrustManager instance. 244 */ 245 public TrustManager getTrustManager() { 246 return trustManager; 247 } 248 249 /** 250 * Performs a lazy init of the SSL context. 251 * 252 * @throws IOException When could not initialize the SSL context. 253 */ 254 private void initSSLContext() throws IOException { 255 if (context == null) { 256 context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); 257 } 258 } 259 260 /** 261 * Tests whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior is for this to be disabled. 262 * 263 * @return True if enabled, false if not. 264 * @since 3.4 265 */ 266 public boolean isEndpointCheckingEnabled() { 267 return tlsEndpointChecking; 268 } 269 270 /** 271 * SSL/TLS negotiation. Acquires an SSL socket of a connection and carries out handshake processing. 272 * 273 * @throws IOException If server negotiation fails. 274 */ 275 private void performSSLNegotiation() throws IOException { 276 initSSLContext(); 277 278 final SSLSocketFactory ssf = context.getSocketFactory(); 279 final String host = _hostname_ != null ? _hostname_ : getRemoteAddress().getHostAddress(); 280 final int port = getRemotePort(); 281 final SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, true); 282 socket.setEnableSessionCreation(true); 283 socket.setUseClientMode(true); 284 285 if (tlsEndpointChecking) { 286 SSLSocketUtils.enableEndpointNameVerification(socket); 287 } 288 289 if (protocols != null) { 290 socket.setEnabledProtocols(protocols); 291 } 292 if (suites != null) { 293 socket.setEnabledCipherSuites(suites); 294 } 295 socket.startHandshake(); 296 297 // TODO the following setup appears to duplicate that in the super class methods 298 _socket_ = socket; 299 _input_ = socket.getInputStream(); 300 _output_ = socket.getOutputStream(); 301 reader = new CRLFLineReader(new InputStreamReader(_input_, DEFAULT_ENCODING)); 302 writer = new BufferedWriter(new OutputStreamWriter(_output_, DEFAULT_ENCODING)); 303 304 if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { 305 throw new SSLHandshakeException("Hostname doesn't match certificate"); 306 } 307 } 308 309 /** 310 * Sets which particular cipher suites are enabled for use on this connection. Called before server negotiation. 311 * 312 * @param cipherSuites The cipher suites. 313 */ 314 public void setEnabledCipherSuites(final String[] cipherSuites) { 315 suites = cipherSuites.clone(); 316 } 317 318 /** 319 * Sets which particular protocol versions are enabled for use on this connection. I perform setting before a server negotiation. 320 * 321 * @param protocolVersions The protocol versions. 322 */ 323 public void setEnabledProtocols(final String[] protocolVersions) { 324 protocols = protocolVersions.clone(); 325 } 326 327 /** 328 * Sets automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior is for this to be disabled. 329 * 330 * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. 331 * @since 3.4 332 */ 333 public void setEndpointCheckingEnabled(final boolean enable) { 334 tlsEndpointChecking = enable; 335 } 336 337 /** 338 * Sets to override the default {@link HostnameVerifier} to use. 339 * 340 * @param newHostnameVerifier The HostnameVerifier implementation to set or {@code null} to disable. 341 * @since 3.4 342 */ 343 public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier) { 344 hostnameVerifier = newHostnameVerifier; 345 } 346 347 /** 348 * Sets a {@link KeyManager} to use. 349 * 350 * @param newKeyManager The KeyManager implementation to set. 351 * @see org.apache.commons.net.util.KeyManagerUtils 352 */ 353 public void setKeyManager(final KeyManager newKeyManager) { 354 keyManager = newKeyManager; 355 } 356 357 /** 358 * Sets to override the default {@link TrustManager} to use. 359 * 360 * @param newTrustManager The TrustManager implementation to set. 361 * @see org.apache.commons.net.util.TrustManagerUtils 362 */ 363 public void setTrustManager(final TrustManager newTrustManager) { 364 trustManager = newTrustManager; 365 } 366} 367