You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by sa...@apache.org on 2014/11/25 22:39:49 UTC
svn commit: r1641706 - in
/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider:
./ src/main/java/org/apache/geronimo/javamail/store/imap/connection/
src/main/java/org/apache/geronimo/javamail/store/pop3/connection/
src/main/jav...
Author: salyh
Date: Tue Nov 25 21:39:49 2014
New Revision: 1641706
URL: http://svn.apache.org/r1641706
Log:
Fix GERONIMO-5429 GERONIMO-5430 GERONIMO-5873, fixed also APOP authentication for POP3, POP3 store can now use STARTTLS
Modified:
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java
geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml Tue Nov 25 21:39:49 2014
@@ -104,7 +104,8 @@
javax.net.ssl*;resolution:="optional",
javax.security.sasl*;resolution:="optional",
javax.security.auth.callback*;resolution:="optional",
- org.apache.geronimo.mail.james.mime4j.codec
+ org.apache.geronimo.mail.james.mime4j.codec,
+ javax.net
</Import-Package>
</instructions>
<unpackBundle>true</unpackBundle>
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java Tue Nov 25 21:39:49 2014
@@ -141,30 +141,43 @@ public class IMAPConnection extends Mail
// make sure we process these now
processPendingResponses();
-
- // if we're not already using an SSL connection, and we have permission to issue STARTTLS, AND
- // the server supports this, then switch to TLS mode before continuing.
- if (!sslConnection && props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false) && hasCapability(CAPABILITY_STARTTLS)) {
- // if the server supports TLS, then use it for the connection.
- // on our connection.
-
- // tell the server of our intention to start a TLS session
- sendSimpleCommand("STARTTLS");
-
- // The connection is then handled by the superclass level.
- getConnectedTLSSocket();
-
- // create the special reader for pulling the responses.
- reader = new IMAPResponseStream(inputStream);
-
- // the IMAP spec states that the capability response is independent of login state or
- // user, but I'm not sure I believe that to be the case. It doesn't hurt to refresh
- // the information again after establishing a secure connection.
- getCapability();
- // and we need to repeat this check.
- if (extractResponse("PREAUTH") != null) {
- preAuthorized = true;
+
+ boolean requireTLS = props.getBooleanProperty(MAIL_STARTTLS_REQUIRED, false);
+ boolean enableTLS = props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false);
+ boolean serverSupportsTLS = hasCapability(CAPABILITY_STARTTLS);
+
+ // if we're not already using an SSL connection, and we have permission to issue STARTTLS or its even required
+ // try to setup a SSL connection
+ if (!sslConnection && (enableTLS || requireTLS)) {
+
+ //if the server does not support TLS check if its required.
+ //If true then throw an error, if not establish a non SSL connection
+ if(requireTLS && !serverSupportsTLS) {
+ throw new MessagingException("Server doesn't support required transport level security");
+ } else if (serverSupportsTLS){
+ // tell the server of our intention to start a TLS session
+ sendSimpleCommand("STARTTLS");
+
+ // The connection is then handled by the superclass level.
+ getConnectedTLSSocket();
+
+ // create the special reader for pulling the responses.
+ reader = new IMAPResponseStream(inputStream);
+
+ // the IMAP spec states that the capability response is independent of login state or
+ // user, but I'm not sure I believe that to be the case. It doesn't hurt to refresh
+ // the information again after establishing a secure connection.
+ getCapability();
+ // and we need to repeat this check.
+ if (extractResponse("PREAUTH") != null) {
+ preAuthorized = true;
+ }
+ } else {
+ if (debug) {
+ debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+ }
}
+
}
// damn, no login required.
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java Tue Nov 25 21:39:49 2014
@@ -37,6 +37,7 @@ import javax.mail.internet.InternetHeade
import org.apache.geronimo.javamail.authentication.AuthenticatorFactory;
import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseStream;
import org.apache.geronimo.javamail.store.pop3.POP3Constants;
import org.apache.geronimo.javamail.util.CommandFailedException;
import org.apache.geronimo.javamail.util.InvalidCommandException;
@@ -57,7 +58,7 @@ public class POP3Connection extends Mail
static final protected String MAIL_AUTH_ENABLED = "auth.enable";
static final protected String MAIL_RESET_QUIT = "rsetbeforequit";
static final protected String MAIL_DISABLE_TOP = "disabletop";
- static final protected String MAIL_FORGET_TOP = "forgettopheaders";
+ //static final protected String MAIL_FORGET_TOP = "forgettopheaders"; //TODO forgettopheaders
// the initial greeting string, which might be required for APOP authentication.
protected String greeting;
@@ -71,27 +72,22 @@ public class POP3Connection extends Mail
protected PrintWriter writer;
// this connection was closed unexpectedly
protected boolean closed;
- // indicates whether this conneciton is currently logged in. Once
+ // indicates whether this connection is currently logged in. Once
// we send a QUIT, we're finished.
protected boolean loggedIn;
// indicates whether we need to avoid using the TOP command
// when retrieving headers
protected boolean topDisabled = false;
+ // is TLS enabled on our part?
+ protected boolean useTLS = false;
+ // is TLS required on our part?
+ protected boolean requireTLS = false;
/**
* Normal constructor for an POP3Connection() object.
*
- * @param store The store we're associated with (source of parameter values).
- * @param host The target host name of the IMAP server.
- * @param port The target listening port of the server. Defaults to 119 if
- * the port is specified as -1.
- * @param username The login user name (can be null unless authentication is
- * required).
- * @param password Password associated with the userid account. Can be null if
- * authentication is not required.
- * @param sslConnection
- * True if this is targetted as an SSLConnection.
- * @param debug The session debug flag.
+ * @param props The protocol properties abstraction containing our
+ * property modifiers.
*/
public POP3Connection(ProtocolProperties props) {
super(props);
@@ -100,6 +96,11 @@ public class POP3Connection extends Mail
authEnabled = props.getBooleanProperty(MAIL_AUTH_ENABLED, false);
apopEnabled = props.getBooleanProperty(MAIL_APOP_ENABLED, false);
topDisabled = props.getBooleanProperty(MAIL_DISABLE_TOP, false);
+ // and also check for TLS enablement.
+ useTLS = props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false);
+ // and also check if TLS is required.
+ requireTLS = props.getBooleanProperty(MAIL_STARTTLS_REQUIRED, false);
+
}
@@ -121,7 +122,35 @@ public class POP3Connection extends Mail
getConnection();
// consume the welcome line
getWelcome();
-
+
+ // if we're not already using an SSL connection, and we have permission to issue STARTTLS or its even required
+ // try to setup a SSL connection
+ if (!sslConnection && (useTLS || requireTLS)) {
+
+ // tell the server of our intention to start a TLS session
+ POP3Response starttlsResponse = null;
+ try {
+ starttlsResponse = sendCommand("STLS");
+ } catch (CommandFailedException e) {
+
+ }
+
+ //if the server does not support TLS check if its required.
+ //If true then throw an error, if not establish a non SSL connection
+ if(requireTLS && (starttlsResponse == null || starttlsResponse.isError())) {
+ throw new MessagingException("Server doesn't support required transport level security");
+ } else if(starttlsResponse != null && starttlsResponse.getStatus() == POP3Response.OK) {
+ // The connection is then handled by the superclass level.
+ getConnectedTLSSocket();
+ } else {
+ if (debug) {
+ debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+ }
+ }
+ }
+
+ getConnection();
+
// go login with the server
if (login())
{
@@ -154,7 +183,7 @@ public class POP3Connection extends Mail
throw new MessagingException("Unable to obtain a connection to the POP3 server", e);
}
- // The POp3 protocol is inherently a string-based protocol, so we get
+ // The POP3 protocol is inherently a string-based protocol, so we get
// string readers/writers for the connection streams. Note that we explicitly
// set the encoding to ensure that an inappropriate native encoding is not picked up.
try {
@@ -210,7 +239,7 @@ public class POP3Connection extends Mail
}
/**
- * Test if the connnection has been forcibly closed.
+ * Test if the connection has been forcibly closed.
*
* @return True if the server disconnected the connection.
*/
@@ -259,7 +288,7 @@ public class POP3Connection extends Mail
byte[] data = null;
String line;
- MIMEInputReader source = new MIMEInputReader(reader);
+ //MIMEInputReader source = new MIMEInputReader(reader); //TODO unused
try {
line = reader.readLine();
@@ -570,7 +599,7 @@ public class POP3Connection extends Mail
throw new MessagingException("Unable to create MD5 digest", e);
}
// this will throw an exception if it gives an error failure
- sendCommand("APOP " + username + " " + Hex.encode(digest));
+ sendCommand("APOP " + username + " " + new String(Hex.encode(digest)));
// no exception, we must have passed
return true;
}
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java Tue Nov 25 21:39:49 2014
@@ -64,6 +64,7 @@ public class SMTPConnection extends Mail
protected static final String MAIL_SMTP_ALLOW8BITMIME = "allow8bitmime";
protected static final String MAIL_SMTP_REPORT_SUCCESS = "reportsuccess";
protected static final String MAIL_SMTP_STARTTLS_ENABLE = "starttls.enable";
+ protected static final String MAIL_SMTP_STARTTLS_REQUIRED = "starttls.required";
protected static final String MAIL_SMTP_AUTH = "auth";
protected static final String MAIL_SMTP_FROM = "from";
protected static final String MAIL_SMTP_DSN_RET = "dsn.ret";
@@ -83,6 +84,8 @@ public class SMTPConnection extends Mail
protected boolean serverTLS = false;
// is TLS enabled on our part?
protected boolean useTLS = false;
+ // is TLS required on our part?
+ protected boolean requireTLS = false;
// should we use 8BITMIME encoding if supported by the server?
protected boolean use8bit = false;
@@ -98,6 +101,8 @@ public class SMTPConnection extends Mail
reportSuccess = props.getBooleanProperty(MAIL_SMTP_REPORT_SUCCESS, false);
// and also check for TLS enablement.
useTLS = props.getBooleanProperty(MAIL_SMTP_STARTTLS_ENABLE, false);
+ // and also check if TLS is required.
+ requireTLS = props.getBooleanProperty(MAIL_SMTP_STARTTLS_REQUIRED, false);
// and also check for 8bitmime support
use8bit = props.getBooleanProperty(MAIL_SMTP_ALLOW8BITMIME, false);
}
@@ -804,23 +809,31 @@ public class SMTPConnection extends Mail
sendHelo();
}
- if (useTLS) {
- // if we've been told to use TLS, and this server doesn't support
- // it, then this is a failure
- if (!serverTLS) {
+ if (useTLS || requireTLS) {
+ // if we've been told to use TLS
+ // if its not required and server does not support it we establish an unsecure connection
+ //see GERONIMO-5873 and GERONIMO-5430
+ if (requireTLS && !serverTLS) {
+ // if we've been told to use TLS, and this server doesn't support
+ // it, then this is a failure
throw new MessagingException("Server doesn't support required transport level security");
- }
- // if the server supports TLS, then use it for the connection.
- // on our connection.
- getConnectedTLSSocket();
-
- // some servers (gmail is one that I know of) only send a STARTTLS
- // extension message on the
- // first EHLO command. Now that we have the TLS handshaking
- // established, we need to send a
- // second EHLO message to retrieve the AUTH records from the server.
- if (!sendEhlo()) {
- throw new MessagingException("Failure sending EHLO command to SMTP server");
+ } else if (serverTLS){
+ // if the server supports TLS, then use it for the connection.
+ // on our connection.
+ getConnectedTLSSocket();
+
+ // some servers (gmail is one that I know of) only send a STARTTLS
+ // extension message on the
+ // first EHLO command. Now that we have the TLS handshaking
+ // established, we need to send a
+ // second EHLO message to retrieve the AUTH records from the server.
+ if (!sendEhlo()) {
+ throw new MessagingException("Failure sending EHLO command to SMTP server");
+ }
+ } else {
+ if (debug) {
+ debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+ }
}
}
@@ -928,6 +941,27 @@ public class SMTPConnection extends Mail
useTLS = start;
}
+
+ /**
+ * Return the current requireTLS property.
+ *
+ * @return The current requireTLS property.
+ */
+ public boolean getRequireTLS() {
+ return requireTLS;
+ }
+
+
+ /**
+ * Set a new value for the requireTLS property.
+ *
+ * @param require
+ * The new setting.
+ */
+ public void setRequireTLS(boolean require) {
+ requireTLS = require;
+ }
+
/**
* Process an extension string passed back as the EHLP response.
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java Tue Nov 25 21:39:49 2014
@@ -17,32 +17,43 @@
package org.apache.geronimo.javamail.util;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
-import java.net.Socket;
+import java.net.Socket;
import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.StringTokenizer;
+import java.util.StringTokenizer;
import javax.mail.MessagingException;
import javax.mail.Session;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
-
-import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
-import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
-import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
-import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
-import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
-import org.apache.geronimo.javamail.authentication.SASLAuthenticator;
-import org.apache.geronimo.javamail.util.CommandFailedException;
-import org.apache.geronimo.javamail.util.InvalidCommandException;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
+import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
+import org.apache.geronimo.javamail.authentication.SASLAuthenticator;
/**
* Base class for all mail Store/Transport connection. Centralizes management
@@ -59,10 +70,10 @@ public class MailConnection {
/**
* property keys for protocol properties.
*/
- protected static final String MAIL_AUTH = "auth";
protected static final String MAIL_PORT = "port";
protected static final String MAIL_LOCALHOST = "localhost";
protected static final String MAIL_STARTTLS_ENABLE = "starttls.enable";
+ protected static final String MAIL_STARTTLS_REQUIRED = "starttls.required";
protected static final String MAIL_SSL_ENABLE = "ssl.enable";
protected static final String MAIL_TIMEOUT = "timeout";
protected static final String MAIL_SASL_ENABLE = "sasl.enable";
@@ -71,11 +82,19 @@ public class MailConnection {
protected static final String MAIL_SASL_MECHANISMS = "sasl.mechanisms";
protected static final String MAIL_PLAIN_DISABLE = "auth.plain.disable";
protected static final String MAIL_LOGIN_DISABLE = "auth.login.disable";
+
+ protected static final String MAIL_FACTORY = "socketFactory"; //GERONIMO-5429
protected static final String MAIL_FACTORY_CLASS = "socketFactory.class";
protected static final String MAIL_FACTORY_FALLBACK = "socketFactory.fallback";
protected static final String MAIL_FACTORY_PORT = "socketFactory.port";
+
+ protected static final String MAIL_SSL_FACTORY = "ssl.socketFactory"; //GERONIMO-5429
+ protected static final String MAIL_SSL_FACTORY_CLASS = "ssl.socketFactory.class";
+ protected static final String MAIL_SSL_FACTORY_PORT = "ssl.socketFactory.port";
protected static final String MAIL_SSL_PROTOCOLS = "ssl.protocols";
protected static final String MAIL_SSL_CIPHERSUITES = "ssl.ciphersuites";
+ protected static final String MAIL_SSL_TRUST = "ssl.trust";
+
protected static final String MAIL_LOCALADDRESS = "localaddress";
protected static final String MAIL_LOCALPORT = "localport";
protected static final String MAIL_ENCODE_TRACE = "encodetrace";
@@ -167,6 +186,11 @@ public class MailConnection {
// initialize our debug settings from the session
debug = session.getDebug();
debugStream = session.getDebugOut();
+
+ String mailSSLEnable = props.getProperty(MAIL_SSL_ENABLE);
+ if(mailSSLEnable != null) {
+ this.sslConnection = Boolean.valueOf(mailSSLEnable);
+ }
}
@@ -200,7 +224,7 @@ public class MailConnection {
}
}
- // Before we do anything, let's make sure that we succesfully received a host
+ // Before we do anything, let's make sure that we successfully received a host
if ( host == null ) {
host = DEFAULT_MAIL_HOST;
}
@@ -292,62 +316,84 @@ public class MailConnection {
protected void getConnectedSocket() throws IOException {
debugOut("Attempting plain socket connection to server " + serverHost + ":" + serverPort);
- // check the properties that control how we connect.
- getConnectionProperties();
-
- // the socket factory can be specified via a session property. By default, we just directly
- // instantiate a socket without using a factory.
- String socketFactory = props.getProperty(MAIL_FACTORY_CLASS);
-
- // make sure the socket is nulled out to start
+ // make sure this is null
socket = null;
-
- // if there is no socket factory defined (normal), we just create a socket directly.
- if (socketFactory == null) {
- socket = new Socket(serverHost, serverPort, localAddress, localPort);
+
+ createSocket(false);
+
+ // if we have a timeout value, set that before returning
+ if (timeout >= 0) {
+ socket.setSoTimeout(timeout);
+ }
+ }
+
+ private boolean createSocketFromFactory(boolean ssl, boolean layer) throws IOException {
+
+ String socketFactoryClass = props.getProperty(ssl?MAIL_SSL_FACTORY_CLASS:MAIL_FACTORY_CLASS);
+
+ if(socketFactoryClass == null) {
+ return false;
}
+
+ // we'll try this with potentially two different factories if we're allowed to fall back.
+ boolean fallback = props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false);
+ int socketFactoryPort = props.getIntProperty(ssl?MAIL_SSL_FACTORY_PORT:MAIL_FACTORY_PORT, -1);
+ Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
+
+ debugOut("Creating "+(ssl?"":"non-")+"SSL socket using factory " + socketFactoryClass+ " listening on port "+portArg);
- else {
+ while (true) {
try {
- int socketFactoryPort = props.getIntProperty(MAIL_FACTORY_PORT, -1);
-
- // we choose the port used by the socket based on overrides.
- Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
-
+
// use the current context loader to resolve this.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
- Class factoryClass = loader.loadClass(socketFactory);
+ Class factoryClass = loader.loadClass(socketFactoryClass);
// done indirectly, we need to invoke the method using reflection.
// This retrieves a factory instance.
- Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
- Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
+ //Method getDefault = factoryClass.getMethod("getDefault", new Class[0]); //TODO check instantiation of socket factory
+ Object defFactory = factoryClass.newInstance();// getDefault.invoke(new Object(), new Object[0]);
// now that we have the factory, there are two different createSocket() calls we use,
// depending on whether we have a localAddress override.
- if (localAddress != null) {
+ if (localAddress != null && !layer) {
// retrieve the createSocket(String, int, InetAddress, int) method.
Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
Object[] createSocketArgs = new Object[] { serverHost, portArg, localAddress, new Integer(localPort) };
socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+ break;
}
else {
- // retrieve the createSocket(String, int) method.
- Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
- Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
- Object[] createSocketArgs = new Object[] { serverHost, portArg };
- socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+ if(layer) {
+ // retrieve the createSocket(String, int) method.
+ Class[] createSocketSig = new Class[] { Socket.class, String.class, Integer.TYPE, Boolean.TYPE };
+ Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+ Object[] createSocketArgs = new Object[] { socket, serverHost, new Integer(serverPort), Boolean.TRUE };
+ socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+ break;
+ } else {
+ // retrieve the createSocket(String, int) method.
+ Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+ Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+ Object[] createSocketArgs = new Object[] { serverHost, portArg };
+ socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+ break;
+ }
+
+
}
} catch (Throwable e) {
- // if a socket factor is specified, then we may need to fall back to a default. This behavior
- // is controlled by (surprise) more session properties.
- if (props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false)) {
- debugOut("First plain socket attempt failed, falling back to default factory", e);
- socket = new Socket(serverHost, serverPort, localAddress, localPort);
+ // if we're allowed to fallback, then use the default factory and try this again. We only
+ // allow this to happen once.
+ if (fallback) {
+ debugOut("First attempt at creating "+(ssl?"":"non-")+"SSL socket failed, falling back to default factory");
+ socketFactoryClass = ssl?"javax.net.ssl.SSLSocketFactory":"javax.net.SocketFactory";
+ fallback = false;
+ continue;
}
// we have an exception. We're going to throw an IOException, which may require unwrapping
// or rewrapping the exception.
@@ -357,8 +403,7 @@ public class MailConnection {
e = ((InvocationTargetException)e).getTargetException();
}
- debugOut("Plain socket creation failure", e);
-
+ debugOut("Failure creating "+(ssl?"":"non-")+"SSL socket", e);
// throw this as an IOException, with the original exception attached.
IOException ioe = new IOException("Error connecting to " + serverHost + ", " + serverPort);
ioe.initCause(e);
@@ -366,11 +411,108 @@ public class MailConnection {
}
}
}
- // if we have a timeout value, set that before returning
- if (timeout >= 0) {
- socket.setSoTimeout(timeout);
+
+ return true;
+ }
+
+ private void createSocketFromFactory(SocketFactory sf, boolean layer) throws IOException {
+
+ if(sf instanceof SSLSocketFactory && layer) {
+ socket = ((SSLSocketFactory) sf).createSocket(socket, serverHost, serverPort, true);
+ return;
+ }
+
+ if (localAddress != null) {
+ socket = sf.createSocket(serverHost, serverPort, localAddress, localPort);
+ } else
+ {
+ socket = sf.createSocket(serverHost, serverPort);
+ }
+ }
+
+ private boolean createSocketFromConfiguredFactoryInstance(boolean ssl, boolean layer) throws IOException {
+
+
+
+ if (ssl) {
+ Object sfProp = props.getPropertyAsObject(MAIL_SSL_FACTORY);
+ if (sfProp != null && sfProp instanceof SSLSocketFactory) {
+ createSocketFromFactory((SSLSocketFactory) sfProp, layer);
+ debugOut("Creating "+(ssl?"":"non-")+"SSL "+(layer?"layered":"non-layered")+" socket using a instance of factory " + sfProp.getClass()+ " listening");
+ return true;
+ }
+ } else {
+ Object sfProp = props.getPropertyAsObject(MAIL_FACTORY);
+ if (sfProp != null && sfProp instanceof SocketFactory) {
+ createSocketFromFactory((SocketFactory) sfProp, layer);
+ debugOut("Creating "+(ssl?"":"non-")+"SSL "+(layer?"layered":"non-layered")+" socket using a instance of factory " + sfProp.getClass()+ " listening");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void createSSLSocketFromSSLContext(boolean layer) throws IOException{
+
+ debugOut("Creating "+(layer?"layered ":"non-layered ")+"SSL socket using SSL Context");
+
+
+ try {
+ SSLContext sslcontext = SSLContext.getInstance("TLS");
+
+ String sslTrust = props.getProperty(MAIL_SSL_TRUST);
+
+ TrustManager trustManager = null;
+
+ if(sslTrust != null) {
+ if(sslTrust.equals("*")) {
+ trustManager = new SSLTrustManager(null, true); //trust all
+ } else
+ {
+ String[] trustedHosts = sslTrust.split("\\s+");
+ trustManager = new SSLTrustManager(trustedHosts, false); //trust some
+
+ if(serverHost == null || serverHost.isEmpty() || !Arrays.asList(trustedHosts).contains(serverHost)) {
+ throw new IOException("Server is not trusted: " + serverHost);
+ }
+
+ }
+ } else {
+ trustManager = new SSLTrustManager(null, false); //default
+
+ }
+
+ sslcontext.init(null, new TrustManager[]{trustManager}, null);
+
+ createSocketFromFactory(sslcontext.getSocketFactory(), layer);
+ } catch (KeyManagementException e) {
+ //cannot happen
+ throw new IOException(e);
+ } catch (NoSuchAlgorithmException e) {
+ //cannot happen
+ throw new IOException(e);
}
}
+
+ private void createSocket(boolean ssl) throws IOException {
+
+ if(createSocketFromConfiguredFactoryInstance(ssl, false)) {
+ return;
+ }
+
+ if(createSocketFromFactory(ssl, false)) {
+ return;
+ }
+
+ if(!ssl) {
+ createSocketFromFactory(SocketFactory.getDefault(), false);
+ return;
+ }
+
+
+ createSSLSocketFromSSLContext(false);
+ }
/**
@@ -382,78 +524,12 @@ public class MailConnection {
debugOut("Attempting SSL socket connection to server " + serverHost + ":" + serverPort);
// the socket factory can be specified via a protocol property, a session property, and if all else
// fails (which it usually does), we fall back to the standard factory class.
- String socketFactory = props.getProperty(MAIL_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
// make sure this is null
socket = null;
+
+ createSocket(true);
- // we'll try this with potentially two different factories if we're allowed to fall back.
- boolean fallback = props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false);
-
- while (true) {
- try {
- debugOut("Creating SSL socket using factory " + socketFactory);
-
- int socketFactoryPort = props.getIntProperty(MAIL_FACTORY_PORT, -1);
-
- // we choose the port used by the socket based on overrides.
- Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
-
- // use the current context loader to resolve this.
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- Class factoryClass = loader.loadClass(socketFactory);
-
- // done indirectly, we need to invoke the method using reflection.
- // This retrieves a factory instance.
- Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
- Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
- // now that we have the factory, there are two different createSocket() calls we use,
- // depending on whether we have a localAddress override.
-
- if (localAddress != null) {
- // retrieve the createSocket(String, int, InetAddress, int) method.
- Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
- Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
- Object[] createSocketArgs = new Object[] { serverHost, portArg, localAddress, new Integer(localPort) };
- socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
- break;
- }
- else {
- // retrieve the createSocket(String, int) method.
- Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
- Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
- Object[] createSocketArgs = new Object[] { serverHost, portArg };
- socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
- break;
- }
- } catch (Throwable e) {
- // if we're allowed to fallback, then use the default factory and try this again. We only
- // allow this to happen once.
- if (fallback) {
- debugOut("First attempt at creating SSL socket failed, falling back to default factory");
- socketFactory = "javax.net.ssl.SSLSocketFactory";
- fallback = false;
- continue;
- }
- // we have an exception. We're going to throw an IOException, which may require unwrapping
- // or rewrapping the exception.
- else {
- // we have an exception from the reflection, so unwrap the base exception
- if (e instanceof InvocationTargetException) {
- e = ((InvocationTargetException)e).getTargetException();
- }
-
- debugOut("Failure creating SSL socket", e);
- // throw this as an IOException, with the original exception attached.
- IOException ioe = new IOException("Error connecting to " + serverHost + ", " + serverPort);
- ioe.initCause(e);
- throw ioe;
- }
- }
- }
// and set the timeout value
if (timeout >= 0) {
socket.setSoTimeout(timeout);
@@ -497,44 +573,34 @@ public class MailConnection {
try {
// we use the same target and port as the current connection.
- String host = socket.getInetAddress().getHostName();
- int port = socket.getPort();
+ serverHost = socket.getInetAddress().getHostName();
+ serverPort = socket.getPort();
// the socket factory can be specified via a session property. By default, we use
// the native SSL factory.
- String socketFactory = props.getProperty(MAIL_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
-
- // use the current context loader to resolve this.
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- Class factoryClass = loader.loadClass(socketFactory);
-
- // done indirectly, we need to invoke the method using reflection.
- // This retrieves a factory instance.
- Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
- Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
- // now we need to invoke createSocket()
- Class[] createSocketSig = new Class[] { Socket.class, String.class, Integer.TYPE, Boolean.TYPE };
- Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
- Object[] createSocketArgs = new Object[] { socket, host, new Integer(port), Boolean.TRUE };
-
- // and finally create the socket
- Socket sslSocket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+ if(createSocketFromConfiguredFactoryInstance(true, true)) {
+ debugOut("TLS socket factory configured as instance");
+ } else if(createSocketFromFactory(true, true)) {
+ debugOut("TLS socket factory configured as class");
+ } else {
+ debugOut("TLS socket factory from SSLContext");
+ createSSLSocketFromSSLContext(true);
+ }
// if this is an instance of SSLSocket (very common), try setting the protocol to be
// "TLSv1". If this is some other class because of a factory override, we'll just have to
// accept that things will work.
- if (sslSocket instanceof SSLSocket) {
- ((SSLSocket)sslSocket).setEnabledProtocols(new String[] {"TLSv1"} );
- ((SSLSocket)sslSocket).setUseClientMode(true);
- debugOut("Initiating STARTTLS handshake");
- ((SSLSocket)sslSocket).startHandshake();
+ if (socket instanceof SSLSocket) {
+ String[] suites = ((SSLSocket)socket).getSupportedCipherSuites();
+ ((SSLSocket)socket).setEnabledCipherSuites(suites);
+ ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1"} );
+ ((SSLSocket)socket).setUseClientMode(true);
+ debugOut("Initiating STARTTLS handshake");
+ ((SSLSocket)socket).startHandshake();
+ } else {
+ throw new IOException("Socket is not an instance of SSLSocket, maybe wrong configured ssl factory?");
}
-
- // this is our active socket now
- socket = sslSocket;
getConnectionStreams();
debugOut("TLS connection established");
}
@@ -838,4 +904,57 @@ public class MailConnection {
public void setLocalHost(String localHost) {
this.localHost = localHost;
}
+
+ private class SSLTrustManager implements X509TrustManager {
+
+ private final X509TrustManager defaultTrustManager;
+
+ private final boolean trustAll;
+ private final String[] trustedHosts;
+
+ SSLTrustManager(String[] trustedHosts, boolean trustAll) throws IOException{
+ super();
+ this.trustAll = trustAll;
+ this.trustedHosts = trustedHosts;
+
+ try {
+ TrustManagerFactory defaultTrustManagerFactory = TrustManagerFactory.getInstance("X509");
+ defaultTrustManagerFactory.init((KeyStore)null);
+ defaultTrustManager = (X509TrustManager) defaultTrustManagerFactory.getTrustManagers()[0];
+ } catch (NoSuchAlgorithmException e) {
+ //cannot happen
+ throw new IOException(e);
+ } catch (KeyStoreException e) {
+ //cannot happen
+ throw new IOException(e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String)
+ */
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ defaultTrustManager.checkClientTrusted(chain, authType);
+
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String)
+ */
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ if (!trustAll || trustedHosts != null) {
+ defaultTrustManager.checkServerTrusted(chain, authType);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+ */
+ public X509Certificate[] getAcceptedIssuers() {
+ return defaultTrustManager.getAcceptedIssuers();
+ }
+
+ }
}
Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java Tue Nov 25 21:39:49 2014
@@ -45,7 +45,7 @@ public class ProtocolProperties {
this.protocol = protocol;
this.sslConnection = sslConnection;
this.defaultPort = defaultPort;
- // this helps avoid a lot of concatentates when retrieving properties.
+ // this helps avoid a lot of concatenates when retrieving properties.
protocolPrefix = "mail." + protocol + ".";
}
@@ -104,6 +104,21 @@ public class ProtocolProperties {
String fullName = protocolPrefix + name;
return session.getProperty(fullName);
}
+
+ /**
+ * Get a property (as object) associated with this mail protocol.
+ *
+ * @param name The name of the property.
+ *
+ * @return The property value (returns null if the property has not been set).
+ */
+ public Object getPropertyAsObject(String name) {
+ // the name we're given is the least qualified part of the name.
+ // We construct the full property name
+ // using the protocol
+ String fullName = protocolPrefix + name;
+ return session.getProperties().get(fullName);
+ }
/**
* Get a property associated with this mail session. Returns