From 47dfd99d4f53309cdd57f94fa81d9329b86ea9af Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 17 Dec 2012 14:30:51 -0500 Subject: [PATCH] More work --- .../common/FMLFingerprintViolationEvent.java | 24 ++++++++++ .../cpw/mods/fml/common/FMLModContainer.java | 46 +++++++++++++++---- fml/common/cpw/mods/fml/common/Mod.java | 20 ++++++++ .../event/FMLPreInitializationEvent.java | 23 ++++++++++ 4 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 fml/common/cpw/mods/fml/common/FMLFingerprintViolationEvent.java diff --git a/fml/common/cpw/mods/fml/common/FMLFingerprintViolationEvent.java b/fml/common/cpw/mods/fml/common/FMLFingerprintViolationEvent.java new file mode 100644 index 000000000..b292a433a --- /dev/null +++ b/fml/common/cpw/mods/fml/common/FMLFingerprintViolationEvent.java @@ -0,0 +1,24 @@ +package cpw.mods.fml.common; + +import java.io.File; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; + +import cpw.mods.fml.common.event.FMLEvent; + +public class FMLFingerprintViolationEvent extends FMLEvent { + + public final boolean isDirectory; + public final Set fingerprints; + public final File source; + + public FMLFingerprintViolationEvent(boolean isDirectory, File source, ImmutableSet fingerprints) + { + super(); + this.isDirectory = isDirectory; + this.source = source; + this.fingerprints = fingerprints; + } +} diff --git a/fml/common/cpw/mods/fml/common/FMLModContainer.java b/fml/common/cpw/mods/fml/common/FMLModContainer.java index ed6be8ce5..334baad12 100644 --- a/fml/common/cpw/mods/fml/common/FMLModContainer.java +++ b/fml/common/cpw/mods/fml/common/FMLModContainer.java @@ -18,6 +18,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.security.cert.Certificate; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -34,6 +35,8 @@ import com.google.common.base.Throwables; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; @@ -86,10 +89,13 @@ public class FMLModContainer implements ModContainer .put(FMLServerStartedEvent.class, Mod.ServerStarted.class) .put(FMLServerStoppingEvent.class, Mod.ServerStopping.class) .put(IMCEvent.class,Mod.IMCCallback.class) + .put(FMLFingerprintViolationEvent.class, Mod.FingerprintWarning.class) .build(); private static final BiMap, Class> modTypeAnnotations = modAnnotationTypes.inverse(); private String annotationDependencies; private VersionRange minecraftAccepted; + private boolean fingerprintNotPresent; + private Set sourceFingerprints; public FMLModContainer(String className, File modSource, Map modDescriptor) @@ -335,13 +341,6 @@ public class FMLModContainer implements ModContainer return mc.getMetadata(); } }); - -//TODO -// for (Object o : annotations.get(Block.class)) -// { -// Field f = (Field) o; -// f.set(modInstance, GameRegistry.buildBlock(this, f.getType(), f.getAnnotation(Block.class))); -// } } private void parseSimpleFieldAnnotation(SetMultimap annotations, String annotationClassName, Function retreiver) throws IllegalAccessException @@ -408,12 +407,39 @@ public class FMLModContainer implements ModContainer ModClassLoader modClassLoader = event.getModClassLoader(); modClassLoader.addFile(source); Class clazz = Class.forName(className, true, modClassLoader); - ASMDataTable asmHarvestedAnnotations = event.getASMHarvestedData(); - // TODO - asmHarvestedAnnotations.getAnnotationsFor(this); + + Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates(); + int len = 0; + if (certificates != null) + { + len = certificates.length; + } + Builder certBuilder = ImmutableSet.builder(); + for (int i = 0; i < len; i++) + { + certBuilder.add(CertificateHelper.getFingerprint(certificates[i])); + } + + sourceFingerprints = certBuilder.build(); + + String expectedFingerprint = (String) descriptor.get("certificateFingerprint"); + + if (expectedFingerprint != "" && !sourceFingerprints.contains(expectedFingerprint)) + { + Level warnLevel = Level.SEVERE; + if (source.isDirectory()) + { + warnLevel = Level.FINER; + } + FMLLog.log(warnLevel, "The mod %s is expecting signature %s for source %s, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName()); + } annotations = gatherAnnotations(clazz); isNetworkMod = FMLNetworkHandler.instance().registerNetworkMod(this, clazz, event.getASMHarvestedData()); modInstance = clazz.newInstance(); + if (fingerprintNotPresent) + { + handleModStateEvent(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints))); + } ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); processFieldAnnotations(event.getASMHarvestedData()); } diff --git a/fml/common/cpw/mods/fml/common/Mod.java b/fml/common/cpw/mods/fml/common/Mod.java index 9cf63fa0f..781bf9cb8 100644 --- a/fml/common/cpw/mods/fml/common/Mod.java +++ b/fml/common/cpw/mods/fml/common/Mod.java @@ -100,6 +100,26 @@ public @interface Mod * @return A string listing modids to exclude from loading with this mod. */ String modExclusionList() default ""; + /** + * Specifying this field allows for a mod to expect a signed jar with a fingerprint matching this value. + * The fingerprint should be SHA-1 encoded, lowercase with ':' removed. An empty value indicates that + * the mod is not expecting to be signed. + * + * Any incorrectness of the fingerprint, be it missing or wrong, will result in the {@link FingerprintWarning} + * method firing prior to any other event on the mod. + * + * @return A certificate fingerprint that is expected for this mod. + */ + String certificateFingerprint() default ""; + /** + * Mark the designated method as to be called at if there is something wrong with the certificate fingerprint of + * the mod's jar, or it is missing, or otherwise a problem. + * @author cpw + * + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface FingerprintWarning {} /** * Mark the designated method as being called at the "pre-initialization" phase * @author cpw diff --git a/fml/common/cpw/mods/fml/common/event/FMLPreInitializationEvent.java b/fml/common/cpw/mods/fml/common/event/FMLPreInitializationEvent.java index 0a9d10381..ff4404b85 100644 --- a/fml/common/cpw/mods/fml/common/event/FMLPreInitializationEvent.java +++ b/fml/common/cpw/mods/fml/common/event/FMLPreInitializationEvent.java @@ -1,6 +1,8 @@ package cpw.mods.fml.common.event; import java.io.File; +import java.security.CodeSource; +import java.security.cert.Certificate; import java.util.Properties; import java.util.logging.Logger; @@ -88,4 +90,25 @@ public class FMLPreInitializationEvent extends FMLStateEvent log.setParent(FMLLog.getLogger()); return log; } + + + /** + * Retrieve the FML signing certificates, if any. Validate these against the + * published FML certificates in your mod, if you wish. + * + * @return Certificates used to sign FML and Forge + */ + public Certificate[] getFMLSigningCertificates() + { + CodeSource codeSource = getClass().getClassLoader().getParent().getClass().getProtectionDomain().getCodeSource(); + Certificate[] certs = codeSource.getCertificates(); + if (certs == null) + { + return new Certificate[0]; + } + else + { + return certs; + } + } }