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 @SuppressWarnings("PMD.UseVarargs") //overridden method 069 public void visit(final int version, final int access, final String name, final String signature, 070 final String superName, final String[] interfaces) { 071 super.visit(version, access, name, signature, superName, interfaces); 072 className = name; 073 target = Type.getObjectType(name); 074 } 075 } 076 077 private static final class CustomClassWriter extends ClassWriter { 078 CustomClassWriter(final int flags) { 079 super(flags); 080 } 081 082 CustomClassWriter(final ClassReader classReader, final int flags) { 083 super(classReader, flags); 084 } 085 086 @Override 087 protected String getCommonSuperClass(final String type1, final String type2) { 088 return "java/lang/Object"; 089 } 090 } 091 092 /** 093 * Convenient {@link ClassVisitor} layer to write classfiles into the {@link WeaveEnvironment}. 094 */ 095 class WriteClass extends PrivilizerClassVisitor { 096 097 WriteClass(final ClassReader classReader, final int flags) { 098 super(new CustomClassWriter(classReader, flags)); 099 } 100 101 WriteClass(final int flags) { 102 super(new CustomClassWriter(flags)); 103 } 104 105 @Override 106 public void visitEnd() { 107 super.visitEnd(); 108 final byte[] bytecode = ((ClassWriter) cv).toByteArray(); 109 110 if (verify) { 111 verify(className, bytecode); 112 } 113 114 final DataSource classfile = env.getClassfile(className); 115 env.debug("Writing class %s to resource %s", className, classfile.getName()); 116 OutputStream outputStream = null; 117 try { 118 outputStream = classfile.getOutputStream(); 119 IOUtils.write(bytecode, outputStream); 120 } catch (final IOException e) { 121 throw new RuntimeException(e); 122 } finally { 123 IOUtils.closeQuietly(outputStream); 124 } 125 } 126 } 127 128 /** 129 * Privilizer weaver configuration prefix. 130 */ 131 public static final String CONFIG_WEAVER = "privilizer."; 132 133 /** 134 * {@link AccessLevel} configuration key. 135 * @see AccessLevel#parse(String) 136 */ 137 public static final String CONFIG_ACCESS_LEVEL = CONFIG_WEAVER + "accessLevel"; 138 139 /** 140 * Weave {@link Policy} configuration key. 141 * @see Policy#parse(String) 142 */ 143 public static final String CONFIG_POLICY = CONFIG_WEAVER + "policy"; 144 145 /** 146 * Verification configuration key. 147 * @see BooleanUtils#toBoolean(String) 148 */ 149 public static final String CONFIG_VERIFY = CONFIG_WEAVER + "verify"; 150 151 private static final String GENERATE_NAME = "__privileged_%s"; 152 153 static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; 154 155 final WeaveEnvironment env; 156 final AccessLevel accessLevel; 157 final Policy policy; 158 final boolean verify; 159 160 /** 161 * Create a new {@link Privilizer}. 162 * @param env to use 163 */ 164 public Privilizer(final WeaveEnvironment env) { 165 super(); 166 this.env = env; 167 this.policy = Policy.parse(env.config.getProperty(CONFIG_POLICY)); 168 this.accessLevel = AccessLevel.parse(env.config.getProperty(CONFIG_ACCESS_LEVEL)); 169 verify = BooleanUtils.toBoolean(env.config.getProperty(CONFIG_VERIFY)); 170 } 171 172 String generateName(final String simple) { 173 return String.format(GENERATE_NAME, simple); 174 } 175 176 Type wrap(final Type type) { 177 switch (type.getSort()) { 178 case Type.BOOLEAN: 179 return Type.getType(Boolean.class); 180 case Type.BYTE: 181 return Type.getType(Byte.class); 182 case Type.SHORT: 183 return Type.getType(Short.class); 184 case Type.INT: 185 return Type.getType(Integer.class); 186 case Type.CHAR: 187 return Type.getType(Character.class); 188 case Type.LONG: 189 return Type.getType(Long.class); 190 case Type.FLOAT: 191 return Type.getType(Float.class); 192 case Type.DOUBLE: 193 return Type.getType(Double.class); 194 case Type.VOID: 195 return Type.getType(Void.class); 196 default: 197 return type; 198 } 199 } 200 201 void blueprint(final Class<?> type, final Privilizing privilizing) { 202 final Object[] args = { type.getName(), privilizing }; 203 env.debug("blueprinting class %s %s", args); 204 InputStream bytecode = null; 205 try { 206 bytecode = env.getClassfile(type).getInputStream(); 207 final ClassReader classReader = new ClassReader(bytecode); 208 209 ClassVisitor cvr; 210 cvr = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 211 cvr = new PrivilizingVisitor(this, cvr); 212 cvr = new BlueprintingVisitor(this, cvr, privilizing); 213 214 classReader.accept(cvr, ClassReader.EXPAND_FRAMES); 215 } catch (final Exception e) { 216 throw new RuntimeException(e); 217 } finally { 218 IOUtils.closeQuietly(bytecode); 219 } 220 } 221 222 void privilize(final Class<?> type) { 223 final Object[] args = { type.getName() }; 224 env.debug("privilizing class %s", args); 225 InputStream bytecode = null; 226 try { 227 bytecode = env.getClassfile(type).getInputStream(); 228 final ClassReader classReader = new ClassReader(bytecode); 229 ClassVisitor cv; //NOPMD 230 cv = new WriteClass(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 231 cv = new PrivilizingVisitor(this, cv); 232 233 classReader.accept(cv, ClassReader.EXPAND_FRAMES); 234 } catch (final Exception e) { 235 throw new RuntimeException(e); 236 } finally { 237 IOUtils.closeQuietly(bytecode); 238 } 239 } 240 241 void verify(final String className, final byte[] bytecode) { 242 final ClassReader reader = new ClassReader(bytecode); 243 244 env.debug("Verifying bytecode for class %s", className); 245 final StringWriter w = new StringWriter(); //NOPMD 246 CheckClassAdapter.verify(reader, env.classLoader, false, new PrintWriter(w)); 247 final String error = w.toString(); 248 if (!error.isEmpty()) { 249 env.error(error); 250 final StringWriter trace = new StringWriter(); 251 reader.accept(new TraceClassVisitor(new PrintWriter(trace)), ClassReader.SKIP_DEBUG); 252 env.debug(trace.toString()); 253 throw new IllegalStateException(); 254 } 255 Validate.validState(StringUtils.isBlank(error), error); 256 257 final ClassVisitor checkInnerClasses = new ClassVisitor(Opcodes.ASM5, null) { 258 final Set<String> innerNames = new HashSet<String>(); 259 260 @Override 261 public void visitInnerClass(final String name, final String outerName, final String innerName, 262 final int access) { 263 super.visitInnerClass(name, outerName, innerName, access); 264 Validate.validState(innerNames.add(innerName), "%s already defined", innerName); 265 } 266 }; 267 reader.accept(checkInnerClasses, ClassReader.SKIP_CODE); 268 } 269}