11package cpw .mods .jarhandling .impl ;
22
3+ import sun .misc .Unsafe ;
4+
35import java .lang .reflect .Field ;
4- import java .net . spi . URLStreamHandlerProvider ;
6+ import java .security . CodeSigner ;
57import java .util .Locale ;
8+ import java .util .Map ;
69import java .util .jar .JarInputStream ;
710
811public class SecureJarVerifier {
9- static final Field jarVerifier ;
10- static final Field parsingMeta ;
11- static final Field existingSigners ;
12- static final Field sigFileSigners ;
13- static final Field anyToVerify ;
14-
15- static {
16- final var moduleLayer = ModuleLayer .boot ();
17- final var gj9hOptional = moduleLayer .findModule ("cpw.mods.securejarhandler" );
18- if (gj9hOptional .isPresent ()) {
19- final var gj9h = gj9hOptional .get ();
20- moduleLayer
21- .findModule ("java.base" )
22- .filter (m -> m .isOpen ("java.util.jar" , gj9h ) && m .isExported ("sun.security.util" , gj9h ))
23- .orElseThrow (()->new IllegalStateException ("""
24- Missing JVM arguments. Please correct your runtime profile and run again.
25- --add-opens java.base/java.util.jar=cpw.mods.securejarhandler
26- --add-exports java.base/sun.security.util=cpw.mods.securejarhandler""" ));
27- } else if (Boolean .parseBoolean (System .getProperty ("securejarhandler.throwOnMissingModule" , "true" ))) {
28- // Hack for JMH benchmark: in JMH, SecureJarHandler does not load as a module, but we add-open to all unnamed in the jvm args
29- throw new RuntimeException ("Failed to find securejarhandler module!" );
30- }
31- try {
32- jarVerifier = JarInputStream .class .getDeclaredField ("jv" );
33- sigFileSigners = jarVerifier .getType ().getDeclaredField ("sigFileSigners" );
34- existingSigners = jarVerifier .getType ().getDeclaredField ("verifiedSigners" );
35- parsingMeta = jarVerifier .getType ().getDeclaredField ("parsingMeta" );
36- anyToVerify = jarVerifier .getType ().getDeclaredField ("anyToVerify" );
37- jarVerifier .setAccessible (true );
38- sigFileSigners .setAccessible (true );
39- existingSigners .setAccessible (true );
40- parsingMeta .setAccessible (true );
41- anyToVerify .setAccessible (true );
42- } catch (NoSuchFieldException e ) {
43- throw new IllegalStateException ("Missing essential fields" , e );
44- }
45- }
12+ private static final boolean USE_UNSAAFE = Boolean .parseBoolean (System .getProperty ("securejarhandler.useUnsafeAccessor" , "true" ));
13+ private static IAccessor ACCESSOR = USE_UNSAAFE ? new UnsafeAccessor () : new Reflection ();
4614
4715 private static final char [] LOOKUP = "0123456789abcdef" .toCharArray ();
48-
4916 public static String toHexString (final byte [] bytes ) {
5017 final var buffer = new StringBuffer (2 *bytes .length );
5118 for (int i = 0 , bytesLength = bytes .length ; i < bytesLength ; i ++) {
@@ -65,9 +32,9 @@ public static boolean isSigningRelated(String path) {
6532 if (filename .indexOf ('/' ) != -1 ) // Can't be a sub-directory
6633 return false ;
6734 if ("manifest.mf" .equals (filename ) || // Main manifest, which has the file hashes
68- filename .endsWith (".sf" ) || // Signature file, which has hashes of the entries in the manifest file
69- filename .endsWith (".dsa" ) || // PKCS7 signature, DSA
70- filename .endsWith (".rsa" )) // PKCS7 signature, SHA-256 + RSA
35+ filename .endsWith (".sf" ) || // Signature file, which has hashes of the entries in the manifest file
36+ filename .endsWith (".dsa" ) || // PKCS7 signature, DSA
37+ filename .endsWith (".rsa" )) // PKCS7 signature, SHA-256 + RSA
7138 return true ;
7239
7340 if (!filename .startsWith ("sig-" )) // Unspecifed signature format
@@ -85,4 +52,118 @@ public static boolean isSigningRelated(String path) {
8552 }
8653 return true ;
8754 }
55+
56+ public static Object getJarVerifier (Object inst ) {
57+ return ACCESSOR .getJarVerifier (inst );
58+ }
59+ public static boolean isParsingMeta (Object inst ) { return ACCESSOR .isParsingMeta (inst ); }
60+ public static boolean hasSignatures (Object inst ) { return ACCESSOR .hasSignatures (inst ); }
61+ public static Map <String , CodeSigner []> getVerifiedSigners (Object inst ){ return ACCESSOR .getVerifiedSigners (inst ); }
62+ public static Map <String , CodeSigner []> getPendingSigners (Object inst ){ return ACCESSOR .getPendingSigners (inst ); }
63+
64+ private interface IAccessor {
65+ Object getJarVerifier (Object inst );
66+ boolean isParsingMeta (Object inst );
67+ boolean hasSignatures (Object inst );
68+ Map <String , CodeSigner []> getVerifiedSigners (Object inst );
69+ Map <String , CodeSigner []> getPendingSigners (Object inst );
70+ }
71+
72+ private static class Reflection implements IAccessor {
73+ private static final Field jarVerifier ;
74+ private static final Field parsingMeta ;
75+ private static final Field verifiedSigners ;
76+ private static final Field sigFileSigners ;
77+ private static final Field anyToVerify ;
78+
79+ static {
80+ final var moduleLayer = ModuleLayer .boot ();
81+ final var myModule = moduleLayer .findModule ("cpw.mods.securejarhandler" );
82+ if (myModule .isPresent ()) {
83+ final var gj9h = myModule .get ();
84+ moduleLayer
85+ .findModule ("java.base" )
86+ .filter (m -> m .isOpen ("java.util.jar" , gj9h ) && m .isExported ("sun.security.util" , gj9h ))
87+ .orElseThrow (()->new IllegalStateException ("""
88+ Missing JVM arguments. Please correct your runtime profile and run again.
89+ --add-opens java.base/java.util.jar=cpw.mods.securejarhandler
90+ --add-exports java.base/sun.security.util=cpw.mods.securejarhandler""" ));
91+ } else if (Boolean .parseBoolean (System .getProperty ("securejarhandler.throwOnMissingModule" , "true" ))) {
92+ // Hack for JMH benchmark: in JMH, SecureJarHandler does not load as a module, but we add-open to all unnamed in the jvm args
93+ throw new RuntimeException ("Failed to find securejarhandler module!" );
94+ }
95+ try {
96+ jarVerifier = JarInputStream .class .getDeclaredField ("jv" );
97+ sigFileSigners = jarVerifier .getType ().getDeclaredField ("sigFileSigners" );
98+ verifiedSigners = jarVerifier .getType ().getDeclaredField ("verifiedSigners" );
99+ parsingMeta = jarVerifier .getType ().getDeclaredField ("parsingMeta" );
100+ anyToVerify = jarVerifier .getType ().getDeclaredField ("anyToVerify" );
101+ jarVerifier .setAccessible (true );
102+ sigFileSigners .setAccessible (true );
103+ verifiedSigners .setAccessible (true );
104+ parsingMeta .setAccessible (true );
105+ anyToVerify .setAccessible (true );
106+ } catch (NoSuchFieldException e ) {
107+ throw new IllegalStateException ("Missing essential fields" , e );
108+ }
109+ }
110+
111+ @ Override public Object getJarVerifier (Object inst ) {
112+ return getField (jarVerifier , inst );
113+ }
114+ @ Override public boolean isParsingMeta (Object inst ) { return (Boolean )getField (parsingMeta , inst ); }
115+ @ Override public boolean hasSignatures (Object inst ) { return (Boolean )getField (anyToVerify , inst ); }
116+ @ SuppressWarnings ("unchecked" )
117+ @ Override public Map <String , CodeSigner []> getVerifiedSigners (Object inst ){ return (Map <String , CodeSigner []>)getField (verifiedSigners , inst ); }
118+ @ SuppressWarnings ("unchecked" )
119+ @ Override public Map <String , CodeSigner []> getPendingSigners (Object inst ){ return (Map <String , CodeSigner []>)getField (verifiedSigners , inst ); }
120+
121+ private static Object getField (Field f , Object inst ) {
122+ try {
123+ return f .get (inst );
124+ } catch (IllegalAccessException e ) {
125+ throw new RuntimeException (e );
126+ }
127+ }
128+
129+ }
130+
131+ private static class UnsafeAccessor implements IAccessor {
132+ private static final Unsafe UNSAFE ;
133+ private static final Class <?> JV_TYPE ;
134+ static {
135+ try {
136+ var f = Unsafe .class .getDeclaredField ("theUnsafe" );
137+ f .setAccessible (true );
138+ UNSAFE = (Unsafe )f .get (null );
139+ JV_TYPE = JarInputStream .class .getDeclaredField ("jv" ).getType ();
140+ } catch (Exception e ) {
141+ throw new RuntimeException ("Unable to get Unsafe reference, this should never be possible," +
142+ " be sure to report this will exact details on what JVM you're running." , e );
143+ }
144+ }
145+
146+ private static final long jarVerifier = getOffset (JarInputStream .class , "jv" );
147+ private static final long sigFileSigners = getOffset (JV_TYPE , "sigFileSigners" );
148+ private static final long verifiedSigners = getOffset (JV_TYPE , "verifiedSigners" );
149+ private static final long parsingMeta = getOffset (JV_TYPE , "parsingMeta" );
150+ private static final long anyToVerify = getOffset (JV_TYPE , "anyToVerify" );
151+
152+ private static long getOffset (Class <?> clz , String name ) {
153+ try {
154+ return UNSAFE .objectFieldOffset (clz .getDeclaredField (name ));
155+ } catch (Exception e ) {
156+ throw new RuntimeException ("Unable to get index for " + clz .getName () + "." + name + ", " +
157+ " be sure to report this will exact details on what JVM you're running." , e );
158+ }
159+ }
160+
161+ @ Override public Object getJarVerifier (Object inst ) { return UNSAFE .getObject (inst , jarVerifier ); }
162+ @ Override public boolean isParsingMeta (Object inst ) { return UNSAFE .getBoolean (inst , parsingMeta ); }
163+ @ Override public boolean hasSignatures (Object inst ) { return UNSAFE .getBoolean (inst , anyToVerify ); }
164+ @ SuppressWarnings ("unchecked" )
165+ @ Override public Map <String , CodeSigner []> getVerifiedSigners (Object inst ) { return (Map <String , CodeSigner []>)UNSAFE .getObject (inst , verifiedSigners ); }
166+ @ SuppressWarnings ("unchecked" )
167+ @ Override public Map <String , CodeSigner []> getPendingSigners (Object inst ) { return (Map <String , CodeSigner []>)UNSAFE .getObject (inst , sigFileSigners ); }
168+ }
88169}
0 commit comments