mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-19 07:07:55 +01:00
Xenforo support.
- Added getPassword method in DataSource and all implementations.
This commit is contained in:
parent
aed23cb1ef
commit
bd5d341e67
@ -9,6 +9,7 @@ import com.google.common.cache.RemovalListeners;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.cache.auth.PlayerCache;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -65,6 +66,16 @@ public class CacheDataSource implements DataSource {
|
||||
return getAuth(user) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
user = user.toLowerCase();
|
||||
Optional<PlayerAuth> pAuthOpt = cachedAuths.getIfPresent(user);
|
||||
if (pAuthOpt != null && pAuthOpt.isPresent()) {
|
||||
return pAuthOpt.get().getPassword();
|
||||
}
|
||||
return source.getPassword(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getAuth.
|
||||
*
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -17,6 +18,15 @@ public interface DataSource {
|
||||
*/
|
||||
boolean isAuthAvailable(String user);
|
||||
|
||||
/**
|
||||
* Method getPassword.
|
||||
*
|
||||
* @param user String
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
HashedPassword getPassword(String user);
|
||||
|
||||
/**
|
||||
* Method getAuth.
|
||||
*
|
||||
|
@ -14,6 +14,7 @@ import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.cache.auth.PlayerCache;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
|
||||
/**
|
||||
@ -87,6 +88,15 @@ public class FlatFile implements DataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
PlayerAuth auth = getAuth(user);
|
||||
if (auth != null) {
|
||||
return auth.getPassword();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method saveAuth.
|
||||
*
|
||||
|
@ -6,10 +6,18 @@ import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.security.HashAlgorithm;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.security.crypts.XF;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -244,6 +252,25 @@ public class MySQL implements DataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
try (Connection con = getConnection()) {
|
||||
String sql = "SELECT " + columnPassword + "," + columnSalt + " FROM " + tableName
|
||||
+ " WHERE " + columnName + "=?;";
|
||||
PreparedStatement pst = con.prepareStatement(sql);
|
||||
pst.setString(1, user.toLowerCase());
|
||||
ResultSet rs = pst.executeQuery();
|
||||
if (rs.next()) {
|
||||
return new HashedPassword(rs.getString(columnPassword),
|
||||
!columnSalt.isEmpty() ? rs.getString(columnSalt) : null);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ConsoleLogger.showError(ex.getMessage());
|
||||
ConsoleLogger.writeStackTrace(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized PlayerAuth getAuth(String user) {
|
||||
PlayerAuth pAuth;
|
||||
@ -280,8 +307,7 @@ public class MySQL implements DataSource {
|
||||
if (rs.next()) {
|
||||
Blob blob = rs.getBlob("data");
|
||||
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
||||
// TODO #137: Need to find out how the salt is loaded and need to pass it along to setHash()
|
||||
// pAuth.setPassword(new String(bytes));
|
||||
pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
@ -470,11 +496,9 @@ public class MySQL implements DataSource {
|
||||
rs = pst.executeQuery();
|
||||
if (rs.next()) {
|
||||
int id = rs.getInt(columnID);
|
||||
// Insert password in the correct table
|
||||
pst2 = con.prepareStatement("INSERT INTO xf_user_authenticate (user_id, scheme_class, data) VALUES (?,?,?);");
|
||||
pst2.setInt(1, id);
|
||||
pst2.setString(2, "XenForo_Authentication_Core12");
|
||||
// TODO #137: Need to verify that the salt info is also being passed on...
|
||||
byte[] bytes = auth.getPassword().getHash().getBytes();
|
||||
Blob blob = con.createBlob();
|
||||
blob.setBytes(1, bytes);
|
||||
@ -524,7 +548,6 @@ public class MySQL implements DataSource {
|
||||
// Insert password in the correct table
|
||||
sql = "UPDATE xf_user_authenticate SET data=? WHERE " + columnID + "=?;";
|
||||
PreparedStatement pst2 = con.prepareStatement(sql);
|
||||
// TODO #137: What about the salt?
|
||||
byte[] bytes = auth.getPassword().getHash().getBytes();
|
||||
Blob blob = con.createBlob();
|
||||
blob.setBytes(1, bytes);
|
||||
@ -757,7 +780,7 @@ public class MySQL implements DataSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<String> getAllAuthsByEmail(String email){
|
||||
public synchronized List<String> getAllAuthsByEmail(String email) {
|
||||
List<String> countEmail = new ArrayList<>();
|
||||
try (Connection con = getConnection()) {
|
||||
String sql = "SELECT " + columnName + " FROM " + tableName + " WHERE " + columnEmail + "=?;";
|
||||
@ -920,8 +943,7 @@ public class MySQL implements DataSource {
|
||||
if (rs2.next()) {
|
||||
Blob blob = rs2.getBlob("data");
|
||||
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
||||
// TODO #137: Need to pass the hash and the salt here
|
||||
// pAuth.setPassword(new String(bytes));
|
||||
pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
|
||||
}
|
||||
rs2.close();
|
||||
}
|
||||
@ -968,8 +990,7 @@ public class MySQL implements DataSource {
|
||||
if (rs2.next()) {
|
||||
Blob blob = rs2.getBlob("data");
|
||||
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
||||
// TODO #137: Need to pass the hash and the salt here
|
||||
// pAuth.setHash(new String(bytes));
|
||||
pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
|
||||
}
|
||||
rs2.close();
|
||||
}
|
||||
|
@ -6,7 +6,12 @@ import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -167,6 +172,29 @@ public class SQLite implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
PreparedStatement pst = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
pst = con.prepareStatement("SELECT " + columnPassword + "," + columnSalt
|
||||
+ " FROM " + tableName + " WHERE " + columnName + "=?");
|
||||
pst.setString(1, user);
|
||||
rs = pst.executeQuery();
|
||||
if (rs.next()) {
|
||||
return new HashedPassword(rs.getString(columnPassword),
|
||||
!columnSalt.isEmpty() ? rs.getString(columnSalt) : null);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ConsoleLogger.showError(ex.getMessage());
|
||||
ConsoleLogger.writeStackTrace(ex);
|
||||
} finally {
|
||||
close(rs);
|
||||
close(pst);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getAuth.
|
||||
*
|
||||
|
@ -36,12 +36,8 @@ public class PasswordSecurity {
|
||||
}
|
||||
|
||||
public boolean comparePassword(String password, String playerName) {
|
||||
// TODO ljacqu 20151230: Defining a dataSource.getPassword() method would be more efficient
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth != null) {
|
||||
return comparePassword(password, auth.getPassword(), playerName);
|
||||
}
|
||||
return false;
|
||||
HashedPassword auth = dataSource.getPassword(playerName);
|
||||
return auth != null && comparePassword(password, auth, playerName);
|
||||
}
|
||||
|
||||
public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) {
|
||||
|
@ -15,9 +15,9 @@ package fr.xephi.authme.security.crypts;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||
import fr.xephi.authme.security.crypts.description.SaltType;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
|
||||
@ -25,43 +25,45 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
|
||||
* described in "A Future-Adaptable Password Scheme" by Niels Provos and David
|
||||
* Mazieres.
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* This password hashing system tries to thwart off-line password cracking using
|
||||
* a computationally-intensive hashing algorithm, based on Bruce Schneier's
|
||||
* Blowfish cipher. The work factor of the algorithm is parameterised, so it can
|
||||
* be increased as computers get faster.
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* Usage is really simple. To hash a password for the first time, call the
|
||||
* hashpw method with a random salt, like this:
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* <code>
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br>
|
||||
* </code>
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* To check whether a plaintext password matches one that has been hashed
|
||||
* previously, use the checkpw method:
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* <code>
|
||||
* if (BCrypt.checkpw(candidate_password, stored_hash))<br />
|
||||
* System.out.println("It matches");<br />
|
||||
* else<br />
|
||||
* System.out.println("It does not match");<br />
|
||||
* if (BCrypt.checkpw(candidate_password, stored_hash))<br>
|
||||
* System.out.println("It matches");<br>
|
||||
* else<br>
|
||||
* System.out.println("It does not match");<br>
|
||||
* </code>
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* The gensalt() method takes an optional parameter (log_rounds) that determines
|
||||
* the computational complexity of the hashing:
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* <code>
|
||||
* String strong_salt = BCrypt.gensalt(10)<br />
|
||||
* String stronger_salt = BCrypt.gensalt(12)<br />
|
||||
* String strong_salt = BCrypt.gensalt(10)<br>
|
||||
* String stronger_salt = BCrypt.gensalt(12)<br>
|
||||
* </code>
|
||||
* <p/>
|
||||
* </p><p>
|
||||
* The amount of work increases exponentially (2**log_rounds), so each increment
|
||||
* is twice as much work. The default log_rounds is 10, and the valid range is 4
|
||||
* to 31.
|
||||
* </p>
|
||||
*
|
||||
* @author Damien Miller
|
||||
* @version 0.2
|
||||
@ -102,8 +104,11 @@ public class BCRYPT implements EncryptionMethod {
|
||||
* @param d the byte array to encode
|
||||
* @param len the number of bytes to encode
|
||||
*
|
||||
* @return base64-encoded string * @throws IllegalArgumentException if the length is invalid * @throws IllegalArgumentException
|
||||
* @return base64-encoded string
|
||||
*
|
||||
* @throws IllegalArgumentException if the length is invalid
|
||||
*/
|
||||
|
||||
private static String encode_base64(byte d[], int len)
|
||||
throws IllegalArgumentException {
|
||||
int off = 0;
|
||||
@ -160,6 +165,7 @@ public class BCRYPT implements EncryptionMethod {
|
||||
* @param maxolen the maximum number of bytes to decode
|
||||
*
|
||||
* @return an array containing the decoded bytes
|
||||
*
|
||||
* @throws IllegalArgumentException if maxolen is invalid
|
||||
*/
|
||||
private static byte[] decode_base64(String s, int maxolen)
|
||||
|
@ -4,7 +4,7 @@ package fr.xephi.authme.security.crypts;
|
||||
* The result of a hash computation. See {@link #salt} for details.
|
||||
*/
|
||||
public class HashedPassword {
|
||||
|
||||
|
||||
/** The generated hash. */
|
||||
private final String hash;
|
||||
/**
|
||||
@ -36,13 +36,13 @@ public class HashedPassword {
|
||||
public HashedPassword(String hash) {
|
||||
this(hash, null);
|
||||
}
|
||||
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,74 +1,22 @@
|
||||
package fr.xephi.authme.security.crypts;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class XF implements EncryptionMethod {
|
||||
|
||||
@Override
|
||||
public String computeHash(String password, String salt, String name) {
|
||||
return getSha256(getSha256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword computeHash(String password, String name) {
|
||||
String salt = generateSalt();
|
||||
return new HashedPassword(computeHash(password, salt, null), salt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean comparePassword(String password, HashedPassword hashedPassword, String name) {
|
||||
// TODO #137: Write the comparePassword method. See commit 121d323 for what was here previously; it was
|
||||
// utter non-sense
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO #137: If this method corresponds to HashUtils.sha256(), use it instead of this
|
||||
private String getSha256(String password) {
|
||||
MessageDigest md = null;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
md.update(password.getBytes());
|
||||
byte byteData[] = md.digest();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte element : byteData) {
|
||||
sb.append(Integer.toString((element & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte element : byteData) {
|
||||
String hex = Integer.toHexString(0xff & element);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
public class XF extends BCRYPT {
|
||||
private static final Pattern HASH_PATTERN = Pattern.compile("\"hash\";s.*\"(.*)?\"");
|
||||
|
||||
@Override
|
||||
public String generateSalt() {
|
||||
// TODO #137: Find out what kind of salt format XF uses to generate new passwords
|
||||
return "";
|
||||
return BCRYPT.gensalt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSeparateSalt() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private String regmatch(String pattern, String line) {
|
||||
List<String> allMatches = new ArrayList<>();
|
||||
Matcher m = Pattern.compile(pattern).matcher(line);
|
||||
while (m.find()) {
|
||||
allMatches.add(m.group(1));
|
||||
public static String getHashFromBlob(byte[] blob) {
|
||||
String line = new String(blob);
|
||||
Matcher m = HASH_PATTERN.matcher(line);
|
||||
if (m.find()) {
|
||||
return m.group(1);
|
||||
}
|
||||
return allMatches.get(0);
|
||||
return "*"; // what?
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user