> 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]

Reply via email to