Hi,

attached is my proposed patch to the security service for
Unix crypt authentication. This one needs the cryptix32.jar

Please review. I'd like to get this into Turbine-2 (and Fulcrum?).

Yes, I know I have to add Apache License and docs to UnixCryptAdaptor
and some docs to the libs/README.TXT. It is at the moment just a
proposal.

        Regards
                Henning


Index: turbine-2/conf/TurbineResources.properties
===================================================================
RCS file: /cvs/jakarta/turbine-2/conf/TurbineResources.properties,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- turbine-2/conf/TurbineResources.properties  14 Dec 2001 12:49:25 -0000      1.1.1.1
+++ turbine-2/conf/TurbineResources.properties  14 Dec 2001 15:11:31 -0000      1.2
@@ -832,6 +832,10 @@
 # for encrypting passwords. Check documentation of your JRE for
 # available algorithms.
 #
+# You can also use "unix" or "crypt" if you want to use standard
+# unix crypt(3) encryption which is useful for legacy password
+# databases.
+#
 # Default: SHA
 #
 
Index: turbine-2/src/java/org/apache/turbine/services/security/BaseSecurityService.java
===================================================================
RCS file: 
/cvs/jakarta/turbine-2/src/java/org/apache/turbine/services/security/BaseSecurityService.java,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- turbine-2/src/java/org/apache/turbine/services/security/BaseSecurityService.java   
 14 Dec 2001 12:49:26 -0000      1.1.1.1
+++ turbine-2/src/java/org/apache/turbine/services/security/BaseSecurityService.java   
+ 14 Dec 2001 15:11:31 -0000      1.2
@@ -84,6 +84,8 @@
 import java.io.OutputStream;
 import java.io.ByteArrayOutputStream;
 
+import org.apache.turbine.util.crypt.UnixCryptAdaptor;
+
 /**
  * This is a common subset of SecurityService implementation.
  *
@@ -100,6 +102,7 @@
  * </ul>
  *
  * @author <a href="mailto:[EMAIL PROTECTED]";>Rafal Krzewski</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Henning P. Schmiedehausen</a>
  * @version $Id: BaseSecurityService.java,v 1.1.1.1 2001/08/16 05:09:16 jvanzyl Exp $
  */
 public abstract class BaseSecurityService
@@ -142,8 +145,30 @@
      * @param password the password to process
      * @return processed password
      */
+
     public String encryptPassword( String password )
     {
+        return encryptPassword( password, null );
+    }            
+
+    /**
+     * This method provides client-side encryption of passwords.
+     *
+     * If <code>secure.passwords</code> are enabled in TurbineResources,
+     * the password will be encrypted, if not, it will be returned unchanged.
+     * The <code>secure.passwords.algorithm</code> property can be used
+     * to chose which digest algorithm should be used for performing the
+     * encryption. <code>SHA</code> is used by default.
+     * 
+     * The used algorithms must be prepared to accept null as a valid parameter for 
+salt.
+     *
+     * @param password the password to process
+     * @param salt     algorithms that needs a salt can provide one here
+     * @return processed password
+     */
+
+    public String encryptPassword( String password, String salt )
+    {
         if(password == null)
             return null;
         String secure = getProperties().getProperty(
@@ -156,25 +181,50 @@
         {
             try
             {
-                MessageDigest md = MessageDigest.getInstance(algorithm);
-                // We need to use unicode here, to be independent of platform's
-                // default encoding. Thanks to SGawin for spotting this.
-                byte[] digest = md.digest(password.getBytes("UTF-8"));
-                ByteArrayOutputStream bas = new ByteArrayOutputStream(digest.length + 
digest.length / 3 + 1);
-                OutputStream encodedStream = MimeUtility.encode(bas, "base64");
-                encodedStream.write(digest);
-                return bas.toString();
+                if(algorithm.toLowerCase().equals("unix") || 
+algorithm.toLowerCase().equals("crypt"))
+                {
+                    return UnixCryptAdaptor.crypt(password, salt);
+                }
+                else
+                {
+                    MessageDigest md = MessageDigest.getInstance(algorithm);
+                    // We need to use unicode here, to be independent of platform's
+                    // default encoding. Thanks to SGawin for spotting this.
+                    byte[] digest = md.digest(password.getBytes("UTF-8"));
+                    ByteArrayOutputStream bas = new 
+ByteArrayOutputStream(digest.length + digest.length / 3 + 1);
+                    OutputStream encodedStream = MimeUtility.encode(bas, "base64");
+                    encodedStream.write(digest);
+                    return bas.toString();
+                }
             }
             catch (Exception e)
             {
                 Log.error("Unable to encrypt password."+e.getMessage());
                 Log.error(e);
-
+                
                 return null;
             }
-        } else {
+        } 
+        else 
+        {
             return password;
         }
+    }
+
+
+    /**
+     * Checks if a supplied password matches the encrypted password
+     *
+     * @param checkpw      The clear text password supplied by the user
+     * @param encpw        The current, encrypted password
+     *
+     * @return true if the password matches, else false
+     *
+     */
+    
+    public boolean checkPassword(String checkpw, String encpw)
+    {
+        return encryptPassword(checkpw, encpw).equals(encpw);
     }
 
     /**
Index: turbine-2/src/java/org/apache/turbine/services/security/SecurityService.java
===================================================================
RCS file: 
/cvs/jakarta/turbine-2/src/java/org/apache/turbine/services/security/SecurityService.java,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- turbine-2/src/java/org/apache/turbine/services/security/SecurityService.java       
 14 Dec 2001 12:49:26 -0000      1.1.1.1
+++ turbine-2/src/java/org/apache/turbine/services/security/SecurityService.java       
+ 14 Dec 2001 15:11:31 -0000      1.2
@@ -89,6 +89,7 @@
  * and directory server as the data backend.<br>
  *
  * @author <a href="mailto:[EMAIL PROTECTED]";>Rafal Krzewski</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Henning P. Schmiedehausen</a>
  * @version $Id: SecurityService.java,v 1.1.1.1 2001/08/16 05:09:16 jvanzyl Exp $
  */
 public interface SecurityService 
@@ -274,6 +275,32 @@
      * @return processed password
      */
     public String encryptPassword( String password );
+
+    /**
+     * This method provides client-side encryption mechanism for passwords. 
+     *   
+     * This is an utility method that is used by other classes to maintain
+     * a consistent approach to encrypting password. The behavior of the
+     * method can be configured in service's properties. 
+     *
+     * Algorithms that must supply a salt for encryption can use this method to 
+provide it
+     *
+     * @param password the password to process
+     * @param salt the salt used to encrypt the password
+     * @return processed password
+     */
+    public String encryptPassword( String password, String salt );
+
+    /**
+     * Checks if a supplied password matches the encrypted password
+     *
+     * @param checkpw      The clear text password supplied by the user
+     * @param encpw        The current, encrypted password
+     *
+     * @return true if the password matches, else false
+     *
+     */
+    public boolean checkPassword(String checkpw, String encpw);
 
     /**
      * Change the password for an User.
Index: turbine-2/src/java/org/apache/turbine/services/security/TurbineSecurity.java
===================================================================
RCS file: 
/cvs/jakarta/turbine-2/src/java/org/apache/turbine/services/security/TurbineSecurity.java,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- turbine-2/src/java/org/apache/turbine/services/security/TurbineSecurity.java       
 14 Dec 2001 12:49:26 -0000      1.1.1.1
+++ turbine-2/src/java/org/apache/turbine/services/security/TurbineSecurity.java       
+ 14 Dec 2001 15:11:31 -0000      1.2
@@ -89,6 +89,7 @@
  * named 'global' that can be referenced in the code as {@link 
org.apache.turbine.om.security.Group#GLOBAL_GROUP_NAME}.
  *
  * @author <a href="mailto:[EMAIL PROTECTED]";>Rafal Krzewski</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Henning P. Schmiedehausen</a>
  * @version $Id: TurbineSecurity.java,v 1.1.1.1 2001/08/16 05:09:17 jvanzyl Exp $
  */
 public abstract class TurbineSecurity
@@ -124,6 +125,37 @@
         return getService().encryptPassword(password); 
     }
 
+    /* 
+     * This method provides client-side encryption of passwords. 
+     *   
+     * This is an utility method that is used by other classes to maintain
+     * a consistent approach to encrypting password. The behavior of the
+     * method can be configured in service's properties. 
+     *
+     * @param password the password to process
+     * @param salt the supplied salt to encrypt the password
+     * @return processed password
+     */
+    public static String encryptPassword( String password, String salt )
+    {
+        return getService().encryptPassword(password, salt); 
+    }
+    
+    /**
+     * Checks if a supplied password matches the encrypted password
+     *
+     * @param checkpw      The clear text password supplied by the user
+     * @param encpw        The current, encrypted password
+     *
+     * @return true if the password matches, else false
+     *
+     */
+    
+    public static boolean checkPassword(String checkpw, String encpw)
+    {
+        return getService().checkPassword(checkpw, encpw); 
+    }
+    
     /**
      * Return a Class object representing the system's chosen implementation of
      * of User interface.
Index: turbine-2/src/java/org/apache/turbine/services/security/db/DBUserManager.java
===================================================================
RCS file: 
/cvs/jakarta/turbine-2/src/java/org/apache/turbine/services/security/db/DBUserManager.java,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- turbine-2/src/java/org/apache/turbine/services/security/db/DBUserManager.java      
 14 Dec 2001 12:49:26 -0000      1.1.1.1
+++ turbine-2/src/java/org/apache/turbine/services/security/db/DBUserManager.java      
+ 14 Dec 2001 15:11:31 -0000      1.2
@@ -87,6 +87,7 @@
  * @author <a href="mailto:[EMAIL PROTECTED]";>Frank Y. Kim</a>
  * @author <a href="mailto:[EMAIL PROTECTED]";>Craig D. Berry</a>
  * @author <a href="mailto:[EMAIL PROTECTED]";>Rafal Krzewski</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Henning P. Schmiedehausen</a>
  * @version $Id: DBUserManager.java,v 1.1.1.1 2001/08/16 05:09:19 jvanzyl Exp $
  */
 public class DBUserManager implements UserManager
@@ -300,8 +301,14 @@
             throw new UnknownEntityException("The account '" + 
                 user.getUserName() + "' does not exist");
         }
-        String encrypted = TurbineSecurity.encryptPassword(password);
-        if(!user.getPassword().equals(encrypted))
+
+        /*
+         * Unix crypt needs the existing, encrypted password text as
+         * salt for checking the supplied password. So we supply it 
+         * into the checkPassword routine
+         */
+
+        if(!TurbineSecurity.checkPassword(password, user.getPassword()))
         {
             throw new PasswordMismatchException("The passwords do not match");
         }
@@ -324,13 +331,13 @@
         throws PasswordMismatchException, UnknownEntityException, 
                DataBackendException
     {
-        String encrypted = TurbineSecurity.encryptPassword(oldPassword);
         if(!accountExists(user))
         {
             throw new UnknownEntityException("The account '" + 
                 user.getUserName() + "' does not exist");
         }
-        if(!user.getPassword().equals(encrypted))
+
+        if(!TurbineSecurity.checkPassword(oldPassword, user.getPassword()))
         {
             throw new PasswordMismatchException(
                 "The supplied old password for '" + user.getUserName() +
@@ -389,8 +396,9 @@
             throw new EntityExistsException("The account '" + 
                 user.getUserName() + "' already exists");
         }
-        String encrypted = TurbineSecurity.encryptPassword(initialPassword);
-        user.setPassword(encrypted);
+
+        user.setPassword(TurbineSecurity.encryptPassword(initialPassword));
+
         Criteria criteria = TurbineUserPeer.buildCriteria(user);
         try
         {
Index: turbine-2/src/java/org/apache/turbine/util/crypt/UnixCryptAdaptor.java
===================================================================
RCS file: turbine-2/src/java/org/apache/turbine/util/crypt/UnixCryptAdaptor.java
diff -N turbine-2/src/java/org/apache/turbine/util/crypt/UnixCryptAdaptor.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ turbine-2/src/java/org/apache/turbine/util/crypt/UnixCryptAdaptor.java      14 Dec 
+2001 15:11:31 -0000      1.1
@@ -0,0 +1,32 @@
+package org.apache.turbine.util.crypt;
+
+import org.apache.turbine.util.Log;
+
+import cryptix.tools.UnixCrypt;
+
+public class UnixCryptAdaptor
+{
+    private static final char[] saltChars =
+        
+("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./".toCharArray());
+
+    private UnixCryptAdaptor()
+    {
+    }
+
+    public static final String crypt(String password, String salt)
+        throws Exception
+    {
+        if(salt == null)
+        {
+            java.util.Random randomGenerator = new java.util.Random();
+            int numSaltChars = saltChars.length;
+
+            salt = (new StringBuffer())
+                .append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars])
+                .append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars])
+                .toString();
+        }
+
+        return new UnixCrypt(salt).crypt(password);
+    }
+}
-- 
Dipl.-Inf. (Univ.) Henning P. Schmiedehausen       -- Geschaeftsfuehrer
INTERMETA - Gesellschaft fuer Mehrwertdienste mbH     [EMAIL PROTECTED]

Am Schwabachgrund 22  Fon.: 09131 / 50654-0   [EMAIL PROTECTED]
D-91054 Buckenhof     Fax.: 09131 / 50654-20   

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to