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