189 lines
8.2 KiB
Java
189 lines
8.2 KiB
Java
/*
|
|
* Minecraft Forge
|
|
* Copyright (c) 2016-2018.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package net.minecraftforge.fml.common.asm;
|
|
|
|
import java.io.File;
|
|
import java.io.InputStream;
|
|
import java.net.URLDecoder;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.CodeSource;
|
|
import java.security.cert.Certificate;
|
|
import java.util.Map;
|
|
import java.util.jar.JarEntry;
|
|
import java.util.jar.JarFile;
|
|
|
|
import net.minecraftforge.fml.common.FMLLog;
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
import net.minecraft.launchwrapper.LaunchClassLoader;
|
|
import net.minecraftforge.fml.common.CertificateHelper;
|
|
import net.minecraftforge.fml.common.FMLCommonHandler;
|
|
import net.minecraftforge.fml.common.asm.deobf.FMLDeobfuscatingRemapper;
|
|
import net.minecraftforge.fml.common.patcher.ClassPatchManager;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
|
|
import com.google.common.io.ByteStreams;
|
|
|
|
public class FMLSanityChecker implements IFMLCallHook
|
|
{
|
|
private static final String FMLFINGERPRINT = "51:0A:FB:4C:AF:A4:A0:F2:F5:CF:C5:0E:B4:CC:3C:30:24:4A:E3:8E".toLowerCase().replace(":", "");
|
|
private static final String FORGEFINGERPRINT = "E3:C3:D5:0C:7C:98:6D:F7:4C:64:5C:0A:C5:46:39:74:1C:90:A5:57".toLowerCase().replace(":", "");
|
|
private static final String MCFINGERPRINT = "CD:99:95:96:56:F7:53:DC:28:D8:63:B4:67:69:F7:F8:FB:AE:FC:FC".toLowerCase().replace(":", "");
|
|
private LaunchClassLoader cl;
|
|
private boolean liveEnv;
|
|
public static File fmlLocation;
|
|
|
|
@Override
|
|
public Void call() throws Exception
|
|
{
|
|
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
|
|
boolean goodFML = false;
|
|
boolean fmlIsJar = false;
|
|
if (codeSource.getLocation().getProtocol().equals("jar"))
|
|
{
|
|
fmlIsJar = true;
|
|
Certificate[] certificates = codeSource.getCertificates();
|
|
if (certificates!=null)
|
|
{
|
|
|
|
for (Certificate cert : certificates)
|
|
{
|
|
String fingerprint = CertificateHelper.getFingerprint(cert);
|
|
if (fingerprint.equals(FMLFINGERPRINT))
|
|
{
|
|
FMLLog.log.info("Found valid fingerprint for FML. Certificate fingerprint {}", fingerprint);
|
|
goodFML = true;
|
|
}
|
|
else if (fingerprint.equals(FORGEFINGERPRINT))
|
|
{
|
|
FMLLog.log.info("Found valid fingerprint for Minecraft Forge. Certificate fingerprint {}", fingerprint);
|
|
goodFML = true;
|
|
}
|
|
else
|
|
{
|
|
FMLLog.log.error("Found invalid fingerprint for FML: {}", fingerprint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goodFML = true;
|
|
}
|
|
// Server is not signed, so assume it's good - a deobf env is dev time so it's good too
|
|
boolean goodMC = FMLLaunchHandler.side() == Side.SERVER || !liveEnv;
|
|
int certCount = 0;
|
|
try
|
|
{
|
|
Class<?> cbr = Class.forName("net.minecraft.client.ClientBrandRetriever",false, cl);
|
|
codeSource = cbr.getProtectionDomain().getCodeSource();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// Probably a development environment, or the server (the server is not signed)
|
|
goodMC = true;
|
|
}
|
|
JarFile mcJarFile = null;
|
|
if (fmlIsJar && !goodMC && codeSource.getLocation().getProtocol().equals("jar"))
|
|
{
|
|
try
|
|
{
|
|
String mcPath = codeSource.getLocation().getPath().substring(5);
|
|
mcPath = mcPath.substring(0, mcPath.lastIndexOf('!'));
|
|
mcPath = URLDecoder.decode(mcPath, StandardCharsets.UTF_8.name());
|
|
mcJarFile = new JarFile(mcPath,true);
|
|
mcJarFile.getManifest();
|
|
JarEntry cbrEntry = mcJarFile.getJarEntry("net/minecraft/client/ClientBrandRetriever.class");
|
|
InputStream mcJarFileInputStream = mcJarFile.getInputStream(cbrEntry);
|
|
try
|
|
{
|
|
ByteStreams.toByteArray(mcJarFileInputStream);
|
|
}
|
|
finally
|
|
{
|
|
IOUtils.closeQuietly(mcJarFileInputStream);
|
|
}
|
|
Certificate[] certificates = cbrEntry.getCertificates();
|
|
certCount = certificates != null ? certificates.length : 0;
|
|
if (certificates!=null)
|
|
{
|
|
|
|
for (Certificate cert : certificates)
|
|
{
|
|
String fingerprint = CertificateHelper.getFingerprint(cert);
|
|
if (fingerprint.equals(MCFINGERPRINT))
|
|
{
|
|
FMLLog.log.info("Found valid fingerprint for Minecraft. Certificate fingerprint {}", fingerprint);
|
|
goodMC = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
FMLLog.log.error("A critical error occurred trying to read the minecraft jar file", e);
|
|
}
|
|
finally
|
|
{
|
|
IOUtils.closeQuietly(mcJarFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goodMC = true;
|
|
}
|
|
if (!goodMC)
|
|
{
|
|
FMLLog.log.error("The minecraft jar {} appears to be corrupt! There has been CRITICAL TAMPERING WITH MINECRAFT, it is highly unlikely minecraft will work! STOP NOW, get a clean copy and try again!", codeSource.getLocation().getFile());
|
|
if (!Boolean.parseBoolean(System.getProperty("fml.ignoreInvalidMinecraftCertificates","false")))
|
|
{
|
|
FMLLog.log.error("For your safety, FML will not launch minecraft. You will need to fetch a clean version of the minecraft jar file");
|
|
FMLLog.log.error("Technical information: The class net.minecraft.client.ClientBrandRetriever should have been associated with the minecraft jar file, " +
|
|
"and should have returned us a valid, intact minecraft jar location. This did not work. Either you have modified the minecraft jar file (if so " +
|
|
"run the forge installer again), or you are using a base editing jar that is changing this class (and likely others too). If you REALLY " +
|
|
"want to run minecraft in this configuration, add the flag -Dfml.ignoreInvalidMinecraftCertificates=true to the 'JVM settings' in your launcher profile.");
|
|
FMLCommonHandler.instance().exitJava(1, false);
|
|
}
|
|
else
|
|
{
|
|
FMLLog.log.error("FML has been ordered to ignore the invalid or missing minecraft certificate. This is very likely to cause a problem!");
|
|
FMLLog.log.error("Technical information: ClientBrandRetriever was at {}, there were {} certificates for it", codeSource.getLocation(), certCount);
|
|
}
|
|
}
|
|
if (!goodFML)
|
|
{
|
|
FMLLog.log.error("FML appears to be missing any signature data. This is not a good thing");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void injectData(Map<String, Object> data)
|
|
{
|
|
liveEnv = (Boolean)data.get("runtimeDeobfuscationEnabled");
|
|
cl = (LaunchClassLoader) data.get("classLoader");
|
|
File mcDir = (File)data.get("mcLocation");
|
|
fmlLocation = (File)data.get("coremodLocation");
|
|
ClassPatchManager.INSTANCE.setup(FMLLaunchHandler.side());
|
|
FMLDeobfuscatingRemapper.INSTANCE.setup(mcDir, cl, (String) data.get("deobfuscationFileName"));
|
|
}
|
|
|
|
}
|