Cassifying Zimbra 5

Admin Article

Article Information

This article applies to the following ZCS versions.

ZCS 5.0 Article ZCS 5.0


CASIFYING ZIMBRA COLLABORATION SUITE 5

This document allows guiding you in the process of cassifying Zimbra Collaboration Suite 5.

Installation of the Cas Client

Download the CAS Client Java 2.1.0 from http://www.yale.edu/tp/cas/cas-client-java-2.1.0/webDoc/ .

Untar the downloaded file:

# tar xzvf cas-client-java-2.1.0.tar.gz   

Then copy the CASclient JAR file to Jetty's lib directory

# cp cas-client-java-2.1.10/dist/casclient.jar /opt/zimbra/jetty/common/lib

Configuration of the Server Zimbra

Edit the file "zimbra.web.xml.in" (/opt/zimbra/jetty/etc) Add the following information after sections "<filter>" and "<filter-mapping>" and before the first section of "<servlet>" (by adapting them):

<filter>
   <filter-name>CAS Filter</filter-name>
   <filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
   <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
      <param-value>https://casserver.example.com:8443/cas/login</param-value>
   </init-param>
   <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
      <param-value>https://casserver.example.com:8443/cas/proxyValidate</param-value>
   </init-param>
   <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
      <param-value>zimbraserver.example.com:80</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>CAS Filter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

When we use use both CAS-NTLM and Zimbra we encountered a lot of difficulties and freezes, it seems like zimbra made a lot of CAS requests and some doesn't responds, we modifed the filter as :

<filter-mapping>
   <filter-name>CAS Filter</filter-name>
   <url-pattern>/preauth.jsp</url-pattern>
</filter-mapping>

It reduced the CAS integration to the preauth only. If the user try to acces to http://zimbra.server.com/ it will show the standard login page.

//A ScreenShot is missing

Generation of the SSL_Certificate with Java Keytool

Important Note: It is necessary to delete the certificate if it already exists by using the following commands:

# keytool -delete -alias cas -keystore %JAVA_HOME%/jre/lib/security/cacerts
# Keytool -delete -alias cas -file server.cert 

If you don't know the name of the certificates (or if these commands did not succeed), you can delete the ".keystore" file in root's homedir and "cacerts" files in "/opt/java/jre/lib/security":

# rm /opt/java/jre/lib/security/cacerts 
# rm /root/.keystore 

Generate the certificate by the following command:

# keytool -genkey -alias cas -keypass changeit -keyalg RSA

Enter ‘changeit’ for the password Answer the list of questions. //A ScreenShot is missing

Export the certificate you generated from your personal keystore by the following command:

# keytool -export -alias cas -keypass changeit -file server.cert 

Enter ‘changeit’ for the password. //A ScreenShot is missing

Import the certificate into Java's keystore with this command:

# keytool -import -alias cas -file server.cert -keypass changeit -keystore %JAVA_HOME%/jre/lib/security/cacerts

Enter ‘changeit’ for the password. Answer by 'oui' to add the certificate to the keystore. //A ScreenShot is missing

Validation of the Certificate: Download the class InstallCert.java from http://blogs.sun.com/andreas/resource/InstallCert.java and modify it to comment out some lines :

import java.io.*;
import java.net.URL; 

import java.security.*;
import java.security.cert.*;

import javax.net.ssl.*;

public class InstallCert {

    public static void main(String[] args) throws Exception {
	String host;
	int port;
	char[] passphrase;
	args = new String[1];
	args[0]="cas-server:8443";
	if (args.length == 1 || args.length == 2) {
	    String[] c = args[0].split(":");
	    host = c[0];
	    port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
	    String p = (args.length == 1) ? "changeit" : args[1];
	    passphrase = p.toCharArray();
	} else {
	    System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
	    return;
	}
//for my windows and java
	File file = new File("/opt/zimbra/java/jre/lib/security/cacerts");
//	if (!file.isFile()) {
//	    char SEP = File.separatorChar;
//	   
//	    File dir = new File(System.getProperty("java.home") + SEP
//		    + "lib" + SEP + "security");
//	    file = new File(dir, "jssecacerts");
//	    if (!file.isFile()) {
//		file = new File(dir, "cacerts");
//	    }
//	}
	System.out.println("Loading KeyStore " + file + "...");
	InputStream in = new FileInputStream(file);
	KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
	ks.load(in, passphrase);
	in.close();

	SSLContext context = SSLContext.getInstance("TLS");
	TrustManagerFactory tmf =
	    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
	tmf.init(ks);
	X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
	SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
	context.init(null, new TrustManager[] {tm}, null);
	SSLSocketFactory factory = context.getSocketFactory();
	
	System.out.println("Opening connection to " + host + ":" + port + "...");
	SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
	//socket.setSoTimeout(10000);
	try {
	    System.out.println("Starting SSL handshake...");
	    socket.startHandshake();
	    socket.close();
	    System.out.println();
	    System.out.println("No errors, certificate is already trusted");
	} catch (SSLException e) {
	    System.out.println();
	    e.printStackTrace(System.out);
	}

	X509Certificate[] chain = tm.chain;
	if (chain == null) {
	    System.out.println("Could not obtain server certificate chain");
	    return;
	}

	BufferedReader reader =
		new BufferedReader(new InputStreamReader(System.in));

	System.out.println();
	System.out.println("Server sent " + chain.length + " certificate(s):");
	System.out.println();
	MessageDigest sha1 = MessageDigest.getInstance("SHA1");
	MessageDigest md5 = MessageDigest.getInstance("MD5");
	for (int i = 0; i < chain.length; i++) {
	    X509Certificate cert = chain[i];
	    System.out.println
	    	(" " + (i + 1) + " Subject " + cert.getSubjectDN());
	    System.out.println("   Issuer  " + cert.getIssuerDN());
	    sha1.update(cert.getEncoded());
	    System.out.println("   sha1    " + toHexString(sha1.digest()));
	    md5.update(cert.getEncoded());
	    System.out.println("   md5     " + toHexString(md5.digest()));
	    System.out.println();
	}

	System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
	String line = reader.readLine().trim();
	int k;
	try {
	    k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
	} catch (NumberFormatException e) {
	    System.out.println("KeyStore not changed");
	    return;
	}

	X509Certificate cert = chain[k];
	String alias = host + "-" + (k + 1);
	ks.setCertificateEntry(alias, cert);

	OutputStream out = new FileOutputStream("/opt/zimbra/java/jre/lib/security/cacerts");
	ks.store(out, passphrase);
	out.close();

	System.out.println();
	System.out.println(cert);
	System.out.println();
	System.out.println
		("Added certificate to keystore 'jssecacerts' using alias '"
		+ alias + "'");
	 String gdzie = System.getProperty("java.home//") +
			   "lib//"   + "security";
		    System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		    System.out.println(gdzie);
		    
    }
    
    private static String toHexString(byte[] bytes) {
	StringBuilder sb = new StringBuilder(bytes.length * 3);
	for (int b : bytes) {
	    b &= 0xff;
	    sb.append(Character.forDigit(b >> 4, 16));
	    sb.append(Character.forDigit(b & 0xF, 16));
	    sb.append(' ');
	}
	return sb.toString();
    }

    private static class SavingTrustManager implements X509TrustManager {
	
	private final X509TrustManager tm;
	private X509Certificate[] chain;
	
	SavingTrustManager(X509TrustManager tm) {
	    this.tm = tm;
	}
    
	public X509Certificate[] getAcceptedIssuers() {
	    throw new UnsupportedOperationException();
	}
    
	public void checkClientTrusted(X509Certificate[] chain, String authType)
		throws CertificateException {
	    throw new UnsupportedOperationException();
	}
    
	public void checkServerTrusted(X509Certificate[] chain, String authType)
		throws CertificateException {
	    this.chain = chain;
	    tm.checkServerTrusted(chain, authType);
	}
    }

}

Compile and execute the class downloaded by the commands:

# javac InstallCert.java

Launch the CAS Server and execute the following command:

# java InstallCert 

//A ScreenShot is missing


Answer the composed question by typing "Enter" //A ScreenShot is missing

You now need to get the preAuth key :

# su - zimbra
# zmprov gdpak your_domain_name

The preAuthKey should look like this: "45a9743161d93f5c2e2194890bca9c1452e45078844ea0134e357709bf11a06f"


You can now modify preauth.jsp:

  1. use the preAuthKey you just got for DOMAIN_KEY
  2. put your own domain name to replace "your_domain_name" (end of file)
<%@ page import="java.security.InvalidKeyException" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>
<%@ page import="java.security.SecureRandom" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeSet" %>
<%@ page import="javax.crypto.Mac" %>
<%@ page import="javax.crypto.SecretKey" %>
<%!
 public static final String DOMAIN_KEY =
	"45a9743161d93f5c2e2194890bca9c1452e45078844ea0134e357709bf11a06f";


 public static String generateRedirect(HttpServletRequest request, String name) {
     HashMap params = new HashMap();
     String ts = System.currentTimeMillis()+"";
     params.put("account", name);
     params.put("by", "name"); // needs to be part of hmac
     params.put("timestamp", ts);
     params.put("expires", "0"); // means use the default

     String preAuth = computePreAuth(params, DOMAIN_KEY);
     return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+"/service/preauth/?" +
           "account="+name+
           "&by=name"+
           "&timestamp="+ts+
           "&expires=0"+
           "&preauth="+preAuth;
  }

    public static  String computePreAuth(Map params, String key) {
        StringBuffer sb = new StringBuffer();
        for (Iterator it=new TreeMap(params).values().iterator(); it.hasNext();) {
            if (sb.length() > 0) sb.append('|');
            sb.append(it.next());
        }
        return getHmac(sb.toString(), key.getBytes());        
    }

    private static String getHmac(String data, byte[] key) {
        try {
            ByteKey bk = new ByteKey(key);
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(bk);
            return toHex(mac.doFinal(data.getBytes()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("fatal error", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("fatal error", e);
        }
    }

    static class ByteKey implements SecretKey {
        private byte[] mKey;

        ByteKey(byte[] key) {
            mKey = (byte[]) key.clone();;
        }

        public byte[] getEncoded() {
            return mKey;
        }

        public String getAlgorithm() {
            return "HmacSHA1";
        }

        public String getFormat() {
            return "RAW";
        }
   }

    public static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);
        for (int i=0; i<data.length; i++ ) {
           sb.append(Character.forDigit((data[i] & 0xf0) >>> 4, 16));
           sb.append(Character.forDigit(data[i] & 0x0f, 16) );
        }
        return sb.toString();
    }


%><%
String casUser = (String) session.getAttribute
(edu.yale.its.tp.cas.client.filter.CASFilter.CAS_FILTER_USER);
out.println(casUser);
String redirect = generateRedirect(request, casUser+"@your_domain_name");
out.println(redirect);
response.sendRedirect(redirect);

%>
<html>
<head>
<title>Pre-auth redirect</title>
</head>
<body>

You should never see this page.

</body>
</html>

Then copy this file to /opt/zimbra/jetty/webapps/zimbra


It may be necessary to set the values of zimbraWebClientLoginURL and zimbraWebClientLogoutURL to redirect users to the /preauth.jsp page from http(s)://zimbra.server.com/

#zmprov md example.com zimbraWebClientLoginURL https://zimbra.example.com/zimbra/preauth.jsp
#
#zmprov md example.com zimbraWebClientLogoutURL https://cas.example.com/cas/logout


Restart Zimbra to identify the new "jar" and the files of configuration:

# su - zimbra
# zmcontrol stop
# zmcontrol start


Test the Cassified Zimbra’s Server: Launch the CAS server Launch Zimbra in the Web browser with the url "http://ecm.ecmteggo.tp/zimbra/preauth.jsp" If everything is set up correctly you will be redirected to the CAS server’s login screen.File:Example.jpg

Verified Against: unknown Date Created: 3/18/2008
Article ID: https://wiki.zimbra.com/index.php?title=Cassifying_Zimbra_5 Date Modified: 03/24/2015



Try Zimbra

Try Zimbra Collaboration with a 60-day free trial.
Get it now »

Want to get involved?

You can contribute in the Community, Wiki, Code, or development of Zimlets.
Find out more. »

Looking for a Video?

Visit our YouTube channel to get the latest webinars, technology news, product overviews, and so much more.
Go to the YouTube channel »

Jump to: navigation, search