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