1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.weaver.privilizer;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.PrintWriter;
25 import java.io.StringWriter;
26 import java.util.HashSet;
27 import java.util.Set;
28
29 import org.apache.commons.lang3.BooleanUtils;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.commons.lang3.Validate;
32 import org.apache.commons.weaver.model.WeaveEnvironment;
33 import org.objectweb.asm.ClassReader;
34 import org.objectweb.asm.ClassVisitor;
35 import org.objectweb.asm.ClassWriter;
36 import org.objectweb.asm.Opcodes;
37 import org.objectweb.asm.Type;
38 import org.objectweb.asm.util.CheckClassAdapter;
39 import org.objectweb.asm.util.TraceClassVisitor;
40
41
42
43
44 public class Privilizer {
45
46
47
48 abstract class PrivilizerClassVisitor extends ClassVisitor {
49 String className;
50 Type target;
51
52 protected PrivilizerClassVisitor() {
53 this(null);
54 }
55
56 protected PrivilizerClassVisitor(final ClassVisitor cv) {
57 super(ASM_VERSION, cv);
58 }
59
60 protected Privilizer privilizer() {
61 return Privilizer.this;
62 }
63
64 @Override
65 @SuppressWarnings("PMD.UseVarargs")
66 public void visit(final int version, final int access, final String name, final String signature,
67 final String superName, final String[] interfaces) {
68 super.visit(version, access, name, signature, superName, interfaces);
69 className = name;
70 target = Type.getObjectType(name);
71 }
72 }
73
74 private final class CustomClassWriter extends ClassWriter {
75 CustomClassWriter(final int flags) {
76 super(flags);
77 }
78
79 CustomClassWriter(final ClassReader classReader, final int flags) {
80 super(classReader, flags);
81 }
82
83 @Override
84 protected ClassLoader getClassLoader() {
85 return env.classLoader;
86 }
87 }
88
89
90
91
92 class WriteClass extends PrivilizerClassVisitor {
93
94 WriteClass(final ClassReader classReader, final int flags) {
95 super(new CustomClassWriter(classReader, flags));
96 }
97
98 WriteClass(final int flags) {
99 super(new CustomClassWriter(flags));
100 }
101
102 @Override
103 public void visitEnd() {
104 super.visitEnd();
105 final byte[] bytecode = ((ClassWriter) cv).toByteArray();
106
107 if (verify) {
108 verify(className, bytecode);
109 }
110 final WeaveEnvironment.Resource classfile = env.getClassfile(className);
111 env.debug("Writing class %s to resource %s", className, classfile.getName());
112 try (OutputStream outputStream = classfile.getOutputStream()) {
113 outputStream.write(bytecode);
114 } catch (final IOException e) {
115 throw new IllegalStateException(e);
116 }
117 }
118 }
119
120
121
122
123 public static final String CONFIG_WEAVER = "privilizer.";
124
125
126
127
128
129 public static final String CONFIG_ACCESS_LEVEL = CONFIG_WEAVER + "accessLevel";
130
131
132
133
134
135 public static final String CONFIG_POLICY = CONFIG_WEAVER + "policy";
136
137
138
139
140
141 public static final String CONFIG_VERIFY = CONFIG_WEAVER + "verify";
142
143 private static final String GENERATE_NAME = "__privileged_%s";
144
145 static final int ASM_VERSION = Opcodes.ASM6;
146 static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
147
148 static Type wrap(final Type type) {
149 switch (type.getSort()) {
150 case Type.BOOLEAN:
151 return Type.getType(Boolean.class);
152 case Type.BYTE:
153 return Type.getType(Byte.class);
154 case Type.SHORT:
155 return Type.getType(Short.class);
156 case Type.INT:
157 return Type.getType(Integer.class);
158 case Type.CHAR:
159 return Type.getType(Character.class);
160 case Type.LONG:
161 return Type.getType(Long.class);
162 case Type.FLOAT:
163 return Type.getType(Float.class);
164 case Type.DOUBLE:
165 return Type.getType(Double.class);
166 case Type.VOID:
167 return Type.getType(Void.class);
168 default:
169 return type;
170 }
171 }
172
173 final WeaveEnvironment env;
174 final AccessLevel accessLevel;
175 final Policy policy;
176 final boolean verify;
177
178
179
180
181
182 public Privilizer(final WeaveEnvironment env) {
183 super();
184 this.env = env;
185 this.policy = Policy.parse(env.config.getProperty(CONFIG_POLICY));
186 this.accessLevel = AccessLevel.parse(env.config.getProperty(CONFIG_ACCESS_LEVEL));
187 verify = BooleanUtils.toBoolean(env.config.getProperty(CONFIG_VERIFY));
188 }
189
190 String generateName(final String simple) {
191 return String.format(GENERATE_NAME, simple);
192 }
193
194 void blueprint(final Class<?> type, final Privilizing privilizing) {
195 final Object[] args = { type.getName(), privilizing };
196 env.debug("blueprinting class %s %s", args);
197 try (InputStream bytecode = env.getClassfile(type).getInputStream()) {
198 final ClassReader classReader = new ClassReader(bytecode);
199
200 ClassVisitor cvr;
201 cvr = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
202 cvr = new PrivilizingVisitor(this, cvr);
203 cvr = new BlueprintingVisitor(this, cvr, privilizing);
204
205 classReader.accept(cvr, ClassReader.EXPAND_FRAMES);
206 } catch (final Exception e) {
207 throw new IllegalStateException(e);
208 }
209 }
210
211 void privilize(final Class<?> type) {
212 final Object[] args = { type.getName() };
213 env.debug("privilizing class %s", args);
214 try (InputStream bytecode = env.getClassfile(type).getInputStream()) {
215 final ClassReader classReader = new ClassReader(bytecode);
216 ClassVisitor cv;
217 cv = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
218 cv = new PrivilizingVisitor(this, cv);
219
220 classReader.accept(cv, ClassReader.EXPAND_FRAMES);
221 } catch (final Exception e) {
222 throw new IllegalStateException(e);
223 }
224 }
225
226 void verify(final String className, final byte[] bytecode) {
227 final ClassReader reader = new ClassReader(bytecode);
228
229 env.debug("Verifying bytecode for class %s", className);
230 final StringWriter w = new StringWriter();
231 CheckClassAdapter.verify(reader, env.classLoader, false, new PrintWriter(w));
232 final String error = w.toString();
233 if (!error.isEmpty()) {
234 env.error(error);
235 final StringWriter trace = new StringWriter();
236 reader.accept(new TraceClassVisitor(new PrintWriter(trace)), ClassReader.SKIP_DEBUG);
237 env.debug(trace.toString());
238 throw new IllegalStateException();
239 }
240 Validate.validState(StringUtils.isBlank(error), error);
241
242 final ClassVisitor checkInnerClasses = new ClassVisitor(ASM_VERSION, null) {
243 final Set<String> innerNames = new HashSet<>();
244
245 @Override
246 public void visitInnerClass(final String name, final String outerName, final String innerName,
247 final int access) {
248 super.visitInnerClass(name, outerName, innerName, access);
249 Validate.validState(innerNames.add(innerName), "%s already defined", innerName);
250 }
251 };
252 reader.accept(checkInnerClasses, ClassReader.SKIP_CODE);
253 }
254 }