> Robert Burrell Donkin wrote:
>
> > we'll need to wait until one of the SMTP experts finds some time
> > to comment before we can start reviewing the contribution in detail.
>
> I have asked that he submit the change as a diff (patch) against v2.3.1, and
> will review. I need to create a branch soon, anyway, to maintain a version
> of JAMES suitable for use in production, and will apply this code there for
> testing.
>
> --- Noel
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
>
Hi,
Please find the diff for the smtpserver classes involved and the source for the
new classes.
Kindest regards
Pietro
--- ./smtpserver/EhloCmdHandler.java 2007-01-12 13:56:26.000000000 +0100
+++ EhloCmdHandler.java 2008-02-12 18:50:04.000000000 +0100
@@ -126,7 +126,11 @@
if (maxMessageSize > 0) {
esmtpextensions.add("SIZE " + maxMessageSize);
}
-
+ //NEW FOR SMTP STARTTLS
+ if (!session.isTLSStarted() && session.isStartTlsSupported()) {
+ esmtpextensions.add("STARTTLS");
+ }
+ //END NEW
if (session.isAuthRequired()) {
esmtpextensions.add("AUTH LOGIN PLAIN");
esmtpextensions.add("AUTH=LOGIN PLAIN");
--- ./smtpserver/SMTPHandlerChain.java 2007-01-12 13:56:26.000000000 +0100
+++ SMTPHandlerChain.java 2008-01-28 16:55:51.000000000 +0100
@@ -78,6 +78,9 @@
cmds.setProperty("RCPT" ,RcptCmdHandler.class.getName());
cmds.setProperty("RSET",RsetCmdHandler.class.getName());
cmds.setProperty("VRFY",VrfyCmdHandler.class.getName());
+ //NEW FOR SMTP STARTTLS
+ cmds.setProperty("STARTTLS",StartTlsCmdHandler.class.getName());
+ //
cmds.setProperty("Default
SendMailHandler",SendMailHandler.class.getName());
Enumeration e = cmds.keys();
while (e.hasMoreElements()) {
--- ./smtpserver/SMTPHandlerConfigurationData.java 2007-01-12
13:56:26.000000000 +0100
+++ SMTPHandlerConfigurationData.java 2008-02-12 18:54:07.000000000 +0100
@@ -19,6 +19,8 @@
package org.apache.james.smtpserver;
+import javax.net.ssl.SSLSocketFactory;
+
import org.apache.james.services.MailServer;
import org.apache.james.services.UsersRepository;
@@ -104,4 +106,14 @@
*/
UsersRepository getUsersRepository();
+//NEW FOR STARTTLS
+ boolean isStartTlsSupported();
+
+ /**
+ * Returns the SSLSocketFactory for STARTTLS support.
+ *
+ * @return the ssl socket factory
+ */
+ SSLSocketFactory getSSLSocketFactory();
+//END NEW
}
--- ./smtpserver/SMTPHandler.java 2007-01-12 13:56:26.000000000 +0100
+++ SMTPHandler.java 2008-02-12 18:54:07.000000000 +0100
@@ -34,6 +34,7 @@
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@@ -41,6 +42,12 @@
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
+
+import java.security.KeyStore;
+
+import java.security.Provider;
+import java.security.Security;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@@ -48,6 +55,12 @@
import java.util.Locale;
import java.util.Random;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+
/**
* Provides SMTP functionality by carrying out the server side of the SMTP
* interaction.
@@ -133,6 +146,8 @@
* The writer to which outgoing messages are written.
*/
private PrintWriter out;
+
+
/**
* A Reader wrapper for the incoming stream of bytes coming from the
socket.
@@ -208,6 +223,9 @@
*/
private StringBuffer responseBuffer = new StringBuffer(256);
+// NEW FOR SMTP STARTTLS
+ private boolean TLSStarted=false;
+ private boolean startTlsSupported=false;
/**
* Set the configuration data for the handler
*
@@ -281,6 +299,9 @@
relayingAllowed = theConfigData.isRelayingAllowed(remoteIP);
authRequired = theConfigData.isAuthRequired(remoteIP);
heloEhloEnforcement = theConfigData.useHeloEhloEnforcement();
+ //NEW FOR STARTTLS
+ startTlsSupported= theConfigData.isStartTlsSupported();
+ //END NEW
sessionEnded = false;
resetState();
} catch (Exception e) {
@@ -788,4 +809,39 @@
mode = MESSAGE_ABORT_MODE;
}
+// NEW METHODS FOR SMTP STARTTLS
+ public boolean isTLSStarted() {
+ return TLSStarted;
+ }
+
+ public boolean isStartTlsSupported() {
+ return startTlsSupported;
+ }
+
+ public void secure() throws Exception {
+
+
+ SSLSocket sslsock;
+
+ try {
+ sslsock=(SSLSocket)theConfigData.getSSLSocketFactory().createSocket(
+ socket,
+ socket.getInetAddress().getHostName(),
+ socket.getPort(),
+ true);
+ sslsock.setUseClientMode(false);
+ // just to see SSL negotiated algo
+ getLogger().debug("Finished negotiating SSL - algorithm is " +
+ sslsock.getSession().getCipherSuite());
+ TLSStarted=true;
+ // new socket, in, out, inReader. what about old objects?
+ socket=sslsock;
+ in = new BufferedInputStream(socket.getInputStream(), 1024);
+ inReader = new CRLFTerminatedReader(in, "ASCII");
+ out = new InternetPrintWriter(new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()), 1024), false);
+ } catch (Exception e) {
+ getLogger().error("Error layering SSL over the socket");
+ throw e;
+ }
+ }
}
--- ./smtpserver/SMTPServer.java 2007-01-12 13:56:26.000000000 +0100
+++ SMTPServer.java 2008-02-12 18:54:07.000000000 +0100
@@ -19,6 +19,16 @@
package org.apache.james.smtpserver;
+import java.io.FileInputStream;
+
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
import org.apache.avalon.excalibur.pool.DefaultPool;
import org.apache.avalon.excalibur.pool.HardResourceLimitingPool;
@@ -141,6 +151,11 @@
private ServiceManager serviceManager;
+//NEW FOR STARTTLS
+ private boolean isStartTlsSupported;
+
+ private SSLSocketFactory sslSocketFactory;
+
/**
* @see
org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
*/
@@ -160,6 +175,45 @@
if (isEnabled()) {
mailetcontext.setAttribute(Constants.HELLO_NAME, helloName);
Configuration handlerConfiguration =
configuration.getChild("handler");
+ // NEW FOR STARTTLS
+ String
startTLS=handlerConfiguration.getChild("starttls").getValue("false").trim().toLowerCase();
+ isStartTlsSupported=false;
+ if (startTLS.equals("true")) {
+ isStartTlsSupported=true;
+ System.out.println("starttls="+startTLS);
+ Configuration sslMaterial =
handlerConfiguration.getChild("sslMaterial");
+ SSLMaterial sslm = new SSLMaterial(sslMaterial);
+ KeyStore ks = null;
+ KeyManagerFactory kmf = null;
+ SSLContext sslcontext = null;
+ //just to see SunJCE is loaded
+ Provider[] provs=Security.getProviders();
+ for(int i=0;i<provs.length;i++)
+ getLogger().debug("Provider["+i+"]="+provs[i].getName());
+ // This loads the key material, and initialises the
+ // SSLSocketFactory
+ // Note: in order to load SunJCE provider the jre/lib/ext
should be added
+ // to the java.ext.dirs see the note in run.sh script
+
+ try {
+ ks =
KeyStore.getInstance(sslm.getKeystoreType(),sslm.getKeystoreProvider());
+ ks.load(new FileInputStream(sslm.getKeystoreLocation()),
sslm.getKeystorePassword().toCharArray());
+ kmf = KeyManagerFactory.getInstance(sslm.getAlgorithm(),
sslm.getAlgoProvider());
+ kmf.init(ks, sslm.getKeystorePassword().toCharArray());
+ sslcontext =
SSLContext.getInstance(sslm.getSslProtocol(),sslm.getSslProvider());
+ sslcontext.init(kmf.getKeyManagers(), null, null);
+ sslSocketFactory = sslcontext.getSocketFactory();
+ // just to see the list of supported ciphers
+ String[] ss=sslSocketFactory.getSupportedCipherSuites();
+ getLogger().debug("list of supported ciphers");
+ for(int i=0;i<ss.length;i++)
+ getLogger().debug(ss[i]);
+ } catch (Exception e) {
+ getLogger().error("Exception accessing keystore: " + e);
+ // TODO throw e;
+ }
+ }
+ // END NEW
String authRequiredString =
handlerConfiguration.getChild("authRequired").getValue("false").trim().toLowerCase();
if (authRequiredString.equals("true")) authRequired =
AUTH_REQUIRED;
else if (authRequiredString.equals("announce")) authRequired =
AUTH_ANNOUNCE;
@@ -441,5 +495,19 @@
return SMTPServer.this.heloEhloEnforcement;
}
+//NEW FOR STARTTLS
+ public boolean isStartTlsSupported() {
+ return SMTPServer.this.isStartTlsSupported;
+ }
+
+ /**
+ * Returns the SSLSocketFactory for STARTTLS support.
+ *
+ * @return the ssl socket factory
+ */
+ public SSLSocketFactory getSSLSocketFactory() {
+ return SMTPServer.this.sslSocketFactory;
+ }
+//END NEW
}
}
--- ./smtpserver/SMTPSession.java 2007-01-12 13:56:26.000000000 +0100
+++ SMTPSession.java 2008-02-12 18:50:04.000000000 +0100
@@ -204,7 +204,7 @@
/**
* Sets the user name associated with this SMTP interaction.
*
- * @param userID the user name
+ * @param user the user name
*/
void setUser(String user);
@@ -221,6 +221,12 @@
* @return SMTP session id
*/
String getSessionID();
+
+// NEW METHODS FOR SMTP STARTTLS
+ boolean isStartTlsSupported();
+
+ boolean isTLSStarted();
+ void secure() throws Exception;
}
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.smtpserver;
import org.apache.james.util.mail.dsn.DSNStatus;
/**
* Handles STARTTLS command
*/
public class StartTlsCmdHandler implements CommandHandler {
/**
* The name of the command handled by the command handler
*/
private final static String COMMAND_NAME = "STARTTLS";
/*
* handles STARTTLS command
*
* @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
**/
public void onCommand(SMTPSession session) {
doSTARTTLS(session, session.getCommandArgument());
}
/**
* Handler method called upon receipt of a STARTTLS command.
* Resets message-specific, but not authenticated user, state.
*
* @param session SMTP session object
* @param argument the argument passed in with the command by the SMTP
client
*/
private void doSTARTTLS(SMTPSession session, String argument) {
String responseString = "";
if (!session.isStartTlsSupported()) {
responseString = "500
"+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_UNSUPPORTED)+"
STARTTLS not supported";
}
else if (session.isTLSStarted()) {
responseString = "500
"+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_CMD)+" TLS
already active RFC2487 5.2";
} else if ((argument == null) || (argument.length() == 0)) {
responseString = "220
"+DSNStatus.getStatus(DSNStatus.SUCCESS,DSNStatus.UNDEFINED_STATUS)+" Ready to
start TLS";
} else {
responseString = "501
"+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+"
Syntax error (no parameters allowed) with STARTTLS command";
}
session.writeResponse(responseString);
try {
if(!session.isTLSStarted() && session.isStartTlsSupported()) {
session.secure();
//force reset
session.resetState();
}
} catch (Exception e) {
// TODO
}
}
}
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.smtpserver;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
public class SSLMaterial {
private String keystoreType;
private String keystoreProvider;
private String keystoreLocation;
private String keystorePassword;
private String alias;
private String aliasPassword;
private String algorithm;
private String algoProvider;
private String sslProtocol;
private String sslProvider;
public SSLMaterial(Configuration con) throws ConfigurationException {
keystoreType=con.getChild("keystoreType").getValue("JKS").trim();
keystoreProvider=con.getChild("keystoreProvider").getValue("SUN").trim();
keystoreLocation=con.getChild("keystoreLocation").getValue().trim();
keystorePassword=con.getChild("keystorePassword").getValue().trim();
alias=con.getChild("alias").getValue("").trim();
aliasPassword=con.getChild("aliasPassword").getValue("").trim();
algorithm=con.getChild("algorithm").getValue("SunX509").trim();
algoProvider=con.getChild("algoProvider").getValue("SunJSSE").trim();
sslProtocol=con.getChild("sslProtocol").getValue("SSL").trim();
sslProvider=con.getChild("sslProvider").getValue("SunJSSE").trim();
}
public String getKeystoreType() {
return keystoreType;
}
public String getKeystoreProvider() {
return keystoreProvider;
}
public String getKeystoreLocation() {
return keystoreLocation;
}
public String getKeystorePassword() {
return keystorePassword;
}
public String getAlias() {
return alias;
}
public String getAliasPassword() {
return aliasPassword;
}
public String getAlgorithm() {
return algorithm;
}
public String getAlgoProvider() {
return algoProvider;
}
public String getSslProtocol() {
return sslProtocol;
}
public String getSslProvider() {
return sslProvider;
}
}
Pietro Romanazzi
Responsabile Servizio PEC
Tecnopolis CSATA s.c.r.l.
Centro Tecnico RUPAR Puglia
70010 Valenzano (BA)
e-mail: [EMAIL PROTECTED]
ITALY
Tel: +39 080 4670512
Mob: +39 320 7673652
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]