diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/DataSourceRealm.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/DataSourceRealm.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/DataSourceRealm.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/DataSourceRealm.java 2011-10-05 19:55:41.000000000 -0500 @@ -33,6 +33,7 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.ServerFactory; import org.apache.catalina.core.StandardServer; +import org.apache.catalina.util.HexUtils; import org.apache.catalina.util.StringManager; /** @@ -336,16 +337,9 @@ String credentials) throws SQLException{ String dbCredentials = getPassword(dbConnection, username); - + // Validate the user's credentials - boolean validated = false; - if (hasMessageDigest()) { - // Hex hashes should be compared case-insensitive - validated = (digest(credentials).equalsIgnoreCase(dbCredentials)); - } else - validated = (digest(credentials).equals(dbCredentials)); - - if (validated) { + if (isValidEncodedPassword(credentials, dbCredentials)) { if (containerLog.isTraceEnabled()) containerLog.trace( sm.getString("dataSourceRealm.authenticateSuccess", diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JAASCallbackHandler.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JAASCallbackHandler.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JAASCallbackHandler.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JAASCallbackHandler.java 2011-10-05 11:47:39.000000000 -0500 @@ -69,7 +69,7 @@ this.username = username; if (realm.hasMessageDigest()) { - this.password = realm.digest(password); + this.password = realm.digest(password,null); } else { this.password = password; diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JDBCRealm.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JDBCRealm.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JDBCRealm.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JDBCRealm.java 2011-10-05 19:56:29.000000000 -0500 @@ -29,6 +29,7 @@ import java.util.Properties; import org.apache.catalina.LifecycleException; +import org.apache.catalina.util.HexUtils; import org.apache.catalina.util.StringManager; @@ -413,16 +414,8 @@ // Look up the user's credentials String dbCredentials = getPassword(username); - // Validate the user's credentials - boolean validated = false; - if (hasMessageDigest()) { - // Hex hashes should be compared case-insensitive - validated = (digest(credentials).equalsIgnoreCase(dbCredentials)); - } else { - validated = (digest(credentials).equals(dbCredentials)); - } - - if (validated) { + // validate + if (isValidEncodedPassword(credentials, dbCredentials)) { if (containerLog.isTraceEnabled()) containerLog.trace(sm.getString("jdbcRealm.authenticateSuccess", username)); diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JNDIRealm.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JNDIRealm.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JNDIRealm.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/JNDIRealm.java 2011-10-05 20:08:53.000000000 -0500 @@ -1520,10 +1520,11 @@ } // End synchronized(this) block } else { // Hex hashes should be compared case-insensitive - validated = (digest(credentials).equalsIgnoreCase(password)); + validated = isValidEncodedPassword(credentials, password); } - } else - validated = (digest(credentials).equals(password)); + } else { + validated = isValidEncodedPassword(credentials, password); + } return (validated); } diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/MemoryRealm.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/MemoryRealm.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/MemoryRealm.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/MemoryRealm.java 2011-10-05 19:58:47.000000000 -0500 @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.catalina.LifecycleException; +import org.apache.catalina.util.HexUtils; import org.apache.catalina.util.StringManager; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -147,19 +148,7 @@ GenericPrincipal principal = (GenericPrincipal) principals.get(username); - boolean validated = false; - if (principal != null && credentials != null) { - if (hasMessageDigest()) { - // Hex hashes should be compared case-insensitive - validated = (digest(credentials) - .equalsIgnoreCase(principal.getPassword())); - } else { - validated = - (digest(credentials).equals(principal.getPassword())); - } - } - - if (validated) { + if (principal != null && isValidEncodedPassword(credentials, principal.getPassword())) { if (log.isDebugEnabled()) log.debug(sm.getString("memoryRealm.authenticateSuccess", username)); return (principal); diff -u --recursive orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/RealmBase.java apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/RealmBase.java --- orig/apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/RealmBase.java 2011-08-16 07:26:14.000000000 -0500 +++ apache-tomcat-6.0.33-src/java/org/apache/catalina/realm/RealmBase.java 2011-10-05 20:25:53.000000000 -0500 @@ -28,6 +28,7 @@ import java.security.Principal; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Random; import javax.management.Attribute; import javax.management.MBeanRegistration; @@ -104,6 +105,11 @@ */ protected static final String info = "org.apache.catalina.realm.RealmBase/1.0"; + + /** + * The number of salt bytes are at the end of the encoded password. + */ + protected int saltBytes = 0; /** @@ -227,7 +233,27 @@ } + /** + * Returns the number of salt bytes that are included on the end of the encoded password + * + * @return the number of salt bytes + */ + public int getSaltBytes() { + return saltBytes; + } + + + /** + * Sets the number of salt bytes that are on the end of the password + * + * @param saltBytes the number of salt bytes to use + */ + public void setSaltBytes(int saltBytes) { + this.saltBytes = saltBytes; + } + + /** * Returns the digest encoding charset. * * @return The charset (may be null) for platform default @@ -245,6 +271,7 @@ digestEncoding = charset; } + /** * Return descriptive information about this Realm implementation and * the corresponding version number, in the format @@ -294,6 +321,21 @@ } + protected boolean isValidEncodedPassword(String credentials, String encodedPassword) { + boolean validated ; + if (encodedPassword == null ) { + validated = false; + } else if(hasMessageDigest()) { + byte[] salt = null; + if (saltBytes > 0 && encodedPassword.length() > 2*saltBytes) { + salt = HexUtils.convert(encodedPassword.substring(encodedPassword.length() - 2*saltBytes)); + } + validated = encodedPassword.equalsIgnoreCase(digest(credentials,salt)); + } else { + validated = encodedPassword.equals(credentials); + } + return validated; + } /** * Return the Principal associated with the specified username and @@ -304,18 +346,8 @@ * authenticating this username */ public Principal authenticate(String username, String credentials) { - String serverCredentials = getPassword(username); - - boolean validated ; - if ( serverCredentials == null ) { - validated = false; - } else if(hasMessageDigest()) { - validated = serverCredentials.equalsIgnoreCase(digest(credentials)); - } else { - validated = serverCredentials.equals(credentials); - } - if(! validated ) { + if(!isValidEncodedPassword(credentials, serverCredentials)){ if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateFailure", username)); @@ -1107,16 +1139,17 @@ // ------------------------------------------------------ Protected Methods - /** * Digest the password using the specified algorithm and * convert the result to a corresponding hexadecimal string. * If exception, the plain credentials string is returned. + * If salt is not-null, the message digest is updated with + * that salt and it is appended to the string * * @param credentials Password or other credentials to use in * authenticating this username */ - protected String digest(String credentials) { + protected String digest(String credentials,byte[] salt) { // If no MessageDigest instance is specified, return unchanged if (hasMessageDigest() == false) @@ -1139,6 +1172,10 @@ } } md.update(bytes); + if (salt != null) { + md.update(salt); + return (HexUtils.convert(md.digest())) + HexUtils.convert(salt); + } return (HexUtils.convert(md.digest())); } catch (Exception e) { @@ -1237,7 +1274,7 @@ * @param encoding Character encoding of the string to digest */ public final static String Digest(String credentials, String algorithm, - String encoding) { + String encoding, int saltBytes) { try { // Obtain a new message digest with "digest" encryption @@ -1251,9 +1288,18 @@ } else { md.update(credentials.getBytes(encoding)); } - - // Digest the credentials and return as hexadecimal - return (HexUtils.convert(md.digest())); + if (saltBytes > 0) { + Random r = new Random(); + byte[] salt = new byte[saltBytes]; + for(int i = 0; i < salt.length; i++) { + salt[i] = (byte)r.nextInt(256); + } + md.update(salt); + return (HexUtils.convert(md.digest())) + HexUtils.convert(salt); + } else { + // Digest the credentials and return as hexadecimal + return (HexUtils.convert(md.digest())); + } } catch(Exception ex) { log.error(ex); return credentials; @@ -1270,23 +1316,36 @@ public static void main(String args[]) { String encoding = null; - int firstCredentialArg = 2; - - if (args.length > 4 && args[2].equalsIgnoreCase("-e")) { - encoding = args[3]; - firstCredentialArg = 4; + String algorithm = null; + int saltBytes = 0; + int arg = 0; + + while (arg + 1 < args.length && args[arg].startsWith("-")) { + if (args[arg].equals("-a") && arg + 2 < args.length) { + algorithm = args[arg+1]; + arg += 2; + } else if (args[arg].equals("-e")) { + encoding = args[arg+1]; + arg += 2; + } else if (args[arg].equals("-s")) { + saltBytes = Integer.parseInt(args[arg+1]); + arg += 2; + } else if (args[arg].equals("-")) { // on the off chance that someone needs to hash a password starting with a - + arg++; + break; + } else { + break; + } + } + + if (algorithm == null) { + System.out.println("Usage: RealmBase -a [-s ] [-e ] "); + System.exit(1); + } + for (; arg < args.length; arg++) { + System.out.print(args[arg]+":"); + System.out.println(Digest(args[arg],algorithm,encoding,saltBytes)); } - - if(args.length > firstCredentialArg && args[0].equalsIgnoreCase("-a")) { - for(int i=firstCredentialArg; i < args.length ; i++){ - System.out.print(args[i]+":"); - System.out.println(Digest(args[i], args[1], encoding)); - } - } else { - System.out.println - ("Usage: RealmBase -a [-e ] "); - } - }