Merge branch 'rcaa-master' into master.
diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java index 6d7230f..025b1d8 100644 --- a/src/main/java/com/gitblit/ConfigUserService.java +++ b/src/main/java/com/gitblit/ConfigUserService.java
@@ -898,7 +898,7 @@ user.countryCode = config.getString(USER, username, COUNTRYCODE); user.cookie = config.getString(USER, username, COOKIE); if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) { - user.cookie = StringUtils.getSHA1(user.username + user.password); + user.cookie = user.createCookie(); } // preferences
diff --git a/src/main/java/com/gitblit/auth/AuthenticationProvider.java b/src/main/java/com/gitblit/auth/AuthenticationProvider.java index 0bfe235..e359fd7 100644 --- a/src/main/java/com/gitblit/auth/AuthenticationProvider.java +++ b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
@@ -78,10 +78,10 @@ public abstract AuthenticationType getAuthenticationType(); - protected void setCookie(UserModel user, char [] password) { + protected void setCookie(UserModel user) { // create a user cookie - if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) { - user.cookie = StringUtils.getSHA1(user.username + new String(password)); + if (StringUtils.isEmpty(user.cookie)) { + user.cookie = user.createCookie(); } }
diff --git a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java index 2cdabf6..3a6cb8e 100644 --- a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java +++ b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
@@ -196,7 +196,7 @@ } // create a user cookie - setCookie(user, password); + setCookie(user); // Set user attributes, hide password from backing user service. user.password = Constants.EXTERNAL_ACCOUNT;
diff --git a/src/main/java/com/gitblit/auth/LdapAuthProvider.java b/src/main/java/com/gitblit/auth/LdapAuthProvider.java index 19fd463..c31694b 100644 --- a/src/main/java/com/gitblit/auth/LdapAuthProvider.java +++ b/src/main/java/com/gitblit/auth/LdapAuthProvider.java
@@ -307,7 +307,7 @@ } // create a user cookie - setCookie(user, password); + setCookie(user); if (!supportsTeamMembershipChanges()) { getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
diff --git a/src/main/java/com/gitblit/auth/PAMAuthProvider.java b/src/main/java/com/gitblit/auth/PAMAuthProvider.java index 46f4dd6..b38d49d 100644 --- a/src/main/java/com/gitblit/auth/PAMAuthProvider.java +++ b/src/main/java/com/gitblit/auth/PAMAuthProvider.java
@@ -122,7 +122,7 @@ } // create a user cookie - setCookie(user, password); + setCookie(user); // update user attributes from UnixUser user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java index 27cece2..364aff0 100644 --- a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java +++ b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
@@ -139,7 +139,7 @@ } // create a user cookie - setCookie(user, password); + setCookie(user); // update user attributes from Redmine user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java index df033c2..79c3a0c 100644 --- a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java +++ b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
@@ -66,7 +66,7 @@ user = new UserModel(simpleUsername); } - setCookie(user, password); + setCookie(user); setUserAttributes(user, info); updateUser(user);
diff --git a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java index aee5100..4c31fb1 100644 --- a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java +++ b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
@@ -153,7 +153,7 @@ } // create a user cookie - setCookie(user, password); + setCookie(user); // update user attributes from Windows identity user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/client/EditUserDialog.java b/src/main/java/com/gitblit/client/EditUserDialog.java index 676916b..4b01ff0 100644 --- a/src/main/java/com/gitblit/client/EditUserDialog.java +++ b/src/main/java/com/gitblit/client/EditUserDialog.java
@@ -330,7 +330,7 @@ } // change the cookie - user.cookie = StringUtils.getSHA1(user.username + password); + user.cookie = user.createCookie(); String type = settings.get(Keys.realm.passwordStorage).getString("md5"); if (type.equalsIgnoreCase("md5")) {
diff --git a/src/main/java/com/gitblit/models/UserModel.java b/src/main/java/com/gitblit/models/UserModel.java index e152274..f8f7ed6 100644 --- a/src/main/java/com/gitblit/models/UserModel.java +++ b/src/main/java/com/gitblit/models/UserModel.java
@@ -17,6 +17,7 @@ import java.io.Serializable; import java.security.Principal; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -36,6 +37,7 @@ import com.gitblit.Constants.RegistrantType; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.ModelUtils; +import com.gitblit.utils.SecureRandom; import com.gitblit.utils.StringUtils; /** @@ -52,6 +54,8 @@ public static final UserModel ANONYMOUS = new UserModel(); + private static final SecureRandom RANDOM = new SecureRandom(); + // field names are reflectively mapped in EditUser page public String username; public String password; @@ -660,4 +664,8 @@ String projectPath = StringUtils.getFirstPathElement(repository); return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase(getPersonalPath()); } + + public String createCookie() { + return StringUtils.getSHA1(RANDOM.randomBytes(32)); + } }
diff --git a/src/main/java/com/gitblit/utils/SecureRandom.java b/src/main/java/com/gitblit/utils/SecureRandom.java new file mode 100644 index 0000000..119533d --- /dev/null +++ b/src/main/java/com/gitblit/utils/SecureRandom.java
@@ -0,0 +1,83 @@ +/* + * Copyright 2016 gitblit.com + * + * Licensed 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 com.gitblit.utils; + +/** + * Wrapper class for java.security.SecureRandom, which will periodically reseed + * the PRNG in case an instance of the class has been running for a long time. + * + * @author Florian Zschocke + */ +public class SecureRandom { + + /** Period (in ms) after which a new SecureRandom will be created in order to get a fresh random seed. */ + private static final long RESEED_PERIOD = 24 * 60 * 60 * 1000; /* 24 hours */ + + + private long last; + private java.security.SecureRandom random; + + + + public SecureRandom() { + // Make sure the SecureRandom is seeded right from the start. + // This also lets any blocks during seeding occur at creation + // and prevents it from happening when getting next random bytes. + seed(); + } + + + + public byte[] randomBytes(int num) { + byte[] bytes = new byte[num]; + nextBytes(bytes); + return bytes; + } + + + public void nextBytes(byte[] bytes) { + random.nextBytes(bytes); + reseed(false); + } + + + void reseed(boolean forced) { + long ts = System.currentTimeMillis(); + if (forced || (ts - last) > RESEED_PERIOD) { + last = ts; + runReseed(); + } + } + + + + private void seed() { + random = new java.security.SecureRandom(); + random.nextBytes(new byte[0]); + last = System.currentTimeMillis(); + } + + + private void runReseed() { + // Have some other thread hit the penalty potentially incurred by reseeding, + // so that we can immediately return and not block the operation in progress. + new Thread() { + public void run() { + seed(); + } + }.start(); + } +}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java index 220bee3..72dee6b 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -156,7 +156,7 @@ } // change the cookie - userModel.cookie = StringUtils.getSHA1(userModel.username + password); + userModel.cookie = userModel.createCookie(); // Optionally store the password MD5 digest. String type = app().settings().getString(Keys.realm.passwordStorage, "md5");
diff --git a/src/test/java/com/gitblit/utils/SecureRandomTest.java b/src/test/java/com/gitblit/utils/SecureRandomTest.java new file mode 100644 index 0000000..c4098c2 --- /dev/null +++ b/src/test/java/com/gitblit/utils/SecureRandomTest.java
@@ -0,0 +1,33 @@ +package com.gitblit.utils; + +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; + +public class SecureRandomTest { + + @Test + public void testRandomBytes() { + SecureRandom sr = new SecureRandom(); + byte[] bytes1 = sr.randomBytes(10); + assertEquals(10, bytes1.length); + byte[] bytes2 = sr.randomBytes(10); + assertEquals(10, bytes2.length); + assertFalse(Arrays.equals(bytes1, bytes2)); + + assertEquals(0, sr.randomBytes(0).length); + assertEquals(200, sr.randomBytes(200).length); + } + + @Test + public void testNextBytes() { + SecureRandom sr = new SecureRandom(); + byte[] bytes1 = new byte[32]; + sr.nextBytes(bytes1); + byte[] bytes2 = new byte[32]; + sr.nextBytes(bytes2); + assertFalse(Arrays.equals(bytes1, bytes2)); + } +}