001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.weaver.privilizer; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.PrintWriter; 025import java.io.StringWriter; 026import java.util.HashSet; 027import java.util.Set; 028 029import javax.activation.DataSource; 030 031import org.apache.commons.io.IOUtils; 032import org.apache.commons.lang3.BooleanUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.apache.commons.lang3.Validate; 035import org.apache.commons.weaver.model.WeaveEnvironment; 036import org.objectweb.asm.ClassReader; 037import org.objectweb.asm.ClassVisitor; 038import org.objectweb.asm.ClassWriter; 039import org.objectweb.asm.Opcodes; 040import org.objectweb.asm.Type; 041import org.objectweb.asm.util.CheckClassAdapter; 042import org.objectweb.asm.util.TraceClassVisitor; 043 044/** 045 * Coordinates privilization activities. 046 */ 047public class Privilizer { 048 /** 049 * An ASM {@link ClassVisitor} for privilization. 050 */ 051 abstract class PrivilizerClassVisitor extends ClassVisitor { 052 String className; 053 Type target; 054 055 protected PrivilizerClassVisitor() { 056 this(null); 057 } 058 059 protected PrivilizerClassVisitor(final ClassVisitor cv) { //NOPMD 060 super(Opcodes.ASM5, cv); 061 } 062 063 protected Privilizer privilizer() { 064 return Privilizer.this; 065 } 066 067 @Override 068 public void visit(final int version, final int access, final String name, final String signature, 069 final String superName, final String[] interfaces) { 070 super.visit(version, access, name, signature, superName, interfaces); 071 className = name; 072 target = Type.getObjectType(name); 073 } 074 } 075 076 /** 077 * Necessary to resolve supertypes against WeaveEnvironment ClassLoader. 078 */ 079 private final class CustomClassWriter extends ClassWriter { 080 CustomClassWriter(final int flags) { 081 super(flags); 082 } 083 084 CustomClassWriter(final ClassReader classReader, final int flags) { 085 super(classReader, flags); 086 } 087 088 @Override 089 protected String getCommonSuperClass(final String type1, final String type2) { 090 Class<?> class1; 091 Class<?> class2; 092 try { 093 class1 = Class.forName(type1.replace('/', '.'), false, env.classLoader); 094 class2 = Class.forName(type2.replace('/', '.'), false, env.classLoader); 095 } catch (Exception e) { 096 throw new RuntimeException(e.toString()); 097 } 098 if (class1.isAssignableFrom(class2)) { 099 return type1; 100 } 101 if (class2.isAssignableFrom(class1)) { 102 return type2; 103 } 104 if (class1.isInterface() || class2.isInterface()) { 105 return "java/lang/Object"; 106 } 107 do { 108 class1 = class1.getSuperclass(); 109 } while (!class1.isAssignableFrom(class2)); 110 return class1.getName().replace('.', '/'); 111 } 112 } 113 114 /** 115 * Convenient {@link ClassVisitor} layer to write classfiles into the {@link WeaveEnvironment}. 116 */ 117 class WriteClass extends PrivilizerClassVisitor { 118 119 WriteClass(final ClassReader classReader, final int flags) { 120 super(new CustomClassWriter(classReader, flags)); 121 } 122 123 WriteClass(final int flags) { 124 super(new CustomClassWriter(flags)); 125 } 126 127 @Override 128 public void visitEnd() { 129 super.visitEnd(); 130 final byte[] bytecode = ((ClassWriter) cv).toByteArray(); 131 132 if (verify) { 133 verify(className, bytecode); 134 } 135 136 final DataSource classfile = env.getClassfile(className); 137 env.debug("Writing class %s to resource %s", className, classfile.getName()); 138 OutputStream outputStream = null; 139 try { 140 outputStream = classfile.getOutputStream(); 141 IOUtils.write(bytecode, outputStream); 142 } catch (final IOException e) { 143 throw new RuntimeException(e); 144 } finally { 145 IOUtils.closeQuietly(outputStream); 146 } 147 } 148 } 149 150 /** 151 * Privilizer weaver configuration prefix. 152 */ 153 public static final String CONFIG_WEAVER = "privilizer."; 154 155 /** 156 * {@link AccessLevel} configuration key. 157 * @see AccessLevel#parse(String) 158 */ 159 public static final String CONFIG_ACCESS_LEVEL = CONFIG_WEAVER + "accessLevel"; 160 161 /** 162 * Weave {@link Policy} configuration key. 163 * @see Policy#parse(String) 164 */ 165 public static final String CONFIG_POLICY = CONFIG_WEAVER + "policy"; 166 167 /** 168 * Verification configuration key. 169 * @see BooleanUtils#toBoolean(String) 170 */ 171 public static final String CONFIG_VERIFY = CONFIG_WEAVER + "verify"; 172 173 private static final String GENERATE_NAME = "__privileged_%s"; 174 175 static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; 176 177 final WeaveEnvironment env; 178 final AccessLevel accessLevel; 179 final Policy policy; 180 final boolean verify; 181 182 /** 183 * Create a new {@link Privilizer}. 184 * @param env to use 185 */ 186 public Privilizer(final WeaveEnvironment env) { 187 super(); 188 this.env = env; 189 this.policy = Policy.parse(env.config.getProperty(CONFIG_POLICY)); 190 this.accessLevel = AccessLevel.parse(env.config.getProperty(CONFIG_ACCESS_LEVEL)); 191 verify = BooleanUtils.toBoolean(env.config.getProperty(CONFIG_VERIFY)); 192 } 193 194 String generateName(final String simple) { 195 return String.format(GENERATE_NAME, simple); 196 } 197 198 Type wrap(final Type type) { 199 switch (type.getSort()) { 200 case Type.BOOLEAN: 201 return Type.getType(Boolean.class); 202 case Type.BYTE: 203 return Type.getType(Byte.class); 204 case Type.SHORT: 205 return Type.getType(Short.class); 206 case Type.INT: 207 return Type.getType(Integer.class); 208 case Type.CHAR: 209 return Type.getType(Character.class); 210 case Type.LONG: 211 return Type.getType(Long.class); 212 case Type.FLOAT: 213 return Type.getType(Float.class); 214 case Type.DOUBLE: 215 return Type.getType(Double.class); 216 case Type.VOID: 217 return Type.getType(Void.class); 218 default: 219 return type; 220 } 221 } 222 223 void blueprint(final Class<?> type, final Privilizing privilizing) { 224 final Object[] args = { type.getName(), privilizing }; 225 env.debug("blueprinting class %s %s", args); 226 InputStream bytecode = null; 227 try { 228 bytecode = env.getClassfile(type).getInputStream(); 229 final ClassReader classReader = new ClassReader(bytecode); 230 231 ClassVisitor cvr; 232 cvr = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 233 cvr = new PrivilizingVisitor(this, cvr); 234 cvr = new BlueprintingVisitor(this, cvr, privilizing); 235 236 classReader.accept(cvr, ClassReader.EXPAND_FRAMES); 237 } catch (final Exception e) { 238 throw new RuntimeException(e); 239 } finally { 240 IOUtils.closeQuietly(bytecode); 241 } 242 } 243 244 void privilize(final Class<?> type) { 245 final Object[] args = { type.getName() }; 246 env.debug("privilizing class %s", args); 247 InputStream bytecode = null; 248 try { 249 bytecode = env.getClassfile(type).getInputStream(); 250 final ClassReader classReader = new ClassReader(bytecode); 251 ClassVisitor cv; //NOPMD 252 cv = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 253 cv = new PrivilizingVisitor(this, cv); 254 255 classReader.accept(cv, ClassReader.EXPAND_FRAMES); 256 } catch (final Exception e) { 257 throw new RuntimeException(e); 258 } finally { 259 IOUtils.closeQuietly(bytecode); 260 } 261 } 262 263 void verify(final String className, final byte[] bytecode) { 264 final ClassReader reader = new ClassReader(bytecode); 265 266 env.debug("Verifying bytecode for class %s", className); 267 final StringWriter w = new StringWriter(); //NOPMD 268 CheckClassAdapter.verify(reader, env.classLoader, false, new PrintWriter(w)); 269 final String error = w.toString(); 270 if (!error.isEmpty()) { 271 env.error(error); 272 final StringWriter trace = new StringWriter(); 273 reader.accept(new TraceClassVisitor(new PrintWriter(trace)), ClassReader.SKIP_DEBUG); 274 env.debug(trace.toString()); 275 throw new IllegalStateException(); 276 } 277 Validate.validState(StringUtils.isBlank(error), error); 278 279 final ClassVisitor checkInnerClasses = new ClassVisitor(Opcodes.ASM5, null) { 280 final Set<String> innerNames = new HashSet<String>(); 281 282 @Override 283 public void visitInnerClass(final String name, final String outerName, final String innerName, 284 final int access) { 285 super.visitInnerClass(name, outerName, innerName, access); 286 Validate.validState(innerNames.add(innerName), "%s already defined", innerName); 287 } 288 }; 289 reader.accept(checkInnerClasses, ClassReader.SKIP_CODE); 290 } 291}