mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-09 07:09:41 -04:00
Merge pull request #575 from rdebath/patch-passfix
Fix /pass bypass security issue.
This commit is contained in:
commit
eeb1baab92
@ -21,6 +21,10 @@ using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
#if !DISABLE_OLD_PASSWORDFILE
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace MCGalaxy.Commands.Moderation {
|
||||
public sealed class CmdPass : Command2 {
|
||||
@ -66,6 +70,17 @@ namespace MCGalaxy.Commands.Moderation {
|
||||
p.Message("You are now &averified %Sand have &aaccess to admin commands and features.");
|
||||
p.verifiedPass = true;
|
||||
p.Unverified = false;
|
||||
|
||||
#if !DISABLE_OLD_PASSWORDFILE
|
||||
string oldpath = HashPath(p.name, false);
|
||||
string path = HashPath(p.name, true);
|
||||
if (oldpath != path) {
|
||||
File.Delete(oldpath);
|
||||
byte[] computed = ComputeHash(p.name, password);
|
||||
File.WriteAllBytes(path, computed);
|
||||
p.Message("Your password was &areset for the new format");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
p.passtries++;
|
||||
p.Message("%WWrong Password. %SRemember your password is %Wcase sensitive.");
|
||||
@ -108,16 +123,16 @@ namespace MCGalaxy.Commands.Moderation {
|
||||
}
|
||||
}
|
||||
|
||||
static string HashPath(string name) { return "extra/passwords/" + name + ".dat"; }
|
||||
static bool HasPassword(string name) { return File.Exists(HashPath(name)); }
|
||||
|
||||
static byte[] ComputeHash(string name, string pass) {
|
||||
#if !DISABLE_OLD_PASSWORDFILE
|
||||
static byte[] OldComputeHash(string name, string pass) {
|
||||
// Pointless, but kept for backwards compatibility
|
||||
pass = pass.Replace("<", "(");
|
||||
pass = pass.Replace(">", ")");
|
||||
|
||||
MD5 hash = MD5.Create();
|
||||
byte[] nameB = hash.ComputeHash(Encoding.ASCII.GetBytes(name));
|
||||
// This line means that non-ASCII characters in passwords are
|
||||
// all encoded as the "?" character.
|
||||
byte[] dataB = hash.ComputeHash(Encoding.ASCII.GetBytes(pass));
|
||||
|
||||
byte[] result = new byte[nameB.Length + dataB.Length];
|
||||
@ -125,16 +140,36 @@ namespace MCGalaxy.Commands.Moderation {
|
||||
Array.Copy(dataB, 0, result, nameB.Length, dataB.Length);
|
||||
return hash.ComputeHash(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
static byte[] ComputeHash(string name, string pass) {
|
||||
// The constant string added to the username salt is to mitigate
|
||||
// rainbox tables. We should really have a unique salt for each
|
||||
// user, but this is close enough.
|
||||
return SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("0bec662b-416f-450c-8f50-664fd4a41d49" + name.ToLower() + " " + pass));
|
||||
}
|
||||
|
||||
static bool CheckHash(string name, string pass) {
|
||||
byte[] stored = File.ReadAllBytes(HashPath(name));
|
||||
byte[] computed = ComputeHash(name, pass);
|
||||
|
||||
#if !DISABLE_OLD_PASSWORDFILE
|
||||
// Old style MD5 passwords.
|
||||
byte[] oldcomputed = OldComputeHash(name, pass);
|
||||
if (stored.Length == oldcomputed.Length) {
|
||||
bool oldokay = true;
|
||||
for (int i = 0; i < stored.Length; i++) {
|
||||
if (stored[i] != oldcomputed[i]) oldokay = false;
|
||||
}
|
||||
if (oldokay) return true;
|
||||
}
|
||||
|
||||
// Old passwords stored UTF8 string instead of just the raw 16 byte hashes
|
||||
// We need to support both since this behaviour was accidentally changed
|
||||
if (stored.Length != computed.Length) {
|
||||
return Encoding.UTF8.GetString(stored) == Encoding.UTF8.GetString(computed);
|
||||
}
|
||||
|
||||
#endif
|
||||
for (int i = 0; i < stored.Length; i++) {
|
||||
if (stored[i] != computed[i]) return false;
|
||||
}
|
||||
@ -150,5 +185,42 @@ namespace MCGalaxy.Commands.Moderation {
|
||||
p.Message("%HIf you are an admin, use this command to verify your login.");
|
||||
p.Message("%H You will need to be verified to be able to use commands.");
|
||||
}
|
||||
|
||||
public static bool HasPassword(string name) { return File.Exists(HashPath(name)); }
|
||||
|
||||
#if DISABLE_OLD_PASSWORDFILE
|
||||
static string HashPath(string name) { return "extra/passwords/" + name.ToLower() + ".pwd"; }
|
||||
#else
|
||||
static string HashPath(string name, bool ForUpdate = false)
|
||||
{
|
||||
string PassName = "extra/passwords/" + name.ToLower() + ".pwd";
|
||||
if (ForUpdate || File.Exists(PassName))
|
||||
return PassName;
|
||||
|
||||
string OldName = "extra/passwords/" + name + ".dat";
|
||||
if (File.Exists(PassName))
|
||||
return PassName;
|
||||
|
||||
string directory = Path.GetDirectoryName(OldName);
|
||||
IEnumerable<string> foundFiles = EnumerateFilesCI(directory, OldName);
|
||||
|
||||
if (foundFiles.Count() < 1)
|
||||
return PassName;
|
||||
|
||||
return foundFiles.First();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumerateFilesCI(string directory, string pathAndFileName)
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(directory))
|
||||
{
|
||||
if (String.Equals(file, pathAndFileName, StringComparison.OrdinalIgnoreCase) )
|
||||
{
|
||||
yield return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ namespace MCGalaxy {
|
||||
Unverified = Server.Config.verifyadmins && Rank >= Server.Config.VerifyAdminsRank;
|
||||
if (!Unverified) return;
|
||||
|
||||
if (!File.Exists("extra/passwords/" + name + ".dat")) {
|
||||
if (!Commands.Moderation.CmdPass.HasPassword(name)) {
|
||||
Message("%WPlease set your admin verification password with %T/SetPass [password]!");
|
||||
} else {
|
||||
Message("%WPlease complete admin verification with %T/Pass [password]!");
|
||||
|
Loading…
x
Reference in New Issue
Block a user