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