Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.commons.weaver.privilizer; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import org.objectweb.asm.commons.StaticInitMerger; /** * ASM {@link ClassVisitor} to privilize {@link Privileged} methods. */ class PrivilizingVisitor extends Privilizer.PrivilizerClassVisitor { final Map<Method, String> privilegedMethods = new LinkedHashMap<>(); boolean annotated; final Policy policy; final AccessLevel accessLevel; /** * Create a new {@link PrivilizingVisitor}. * @param privilizer owner * @param cv next */ PrivilizingVisitor(final Privilizer privilizer, final ClassVisitor cv) { //NOPMD privilizer.super(); this.policy = privilizer.policy; this.accessLevel = privilizer.accessLevel; this.cv = new InlineNestedPrivilegedCalls(privilizer, privilegedMethods, new StaticInitMerger(privilizer.generateName("clinit"), cv)); } private void annotate() { if (!annotated) { annotated = true; final AnnotationVisitor privilizedVisitor = super.visitAnnotation( Type.getType(Privilized.class).getDescriptor(), false); privilizedVisitor.visit("value", policy.name()); privilizedVisitor.visitEnd(); } } @Override public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) { annotate(); super.visitInnerClass(name, outerName, innerName, access); } @Override public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { annotate(); return super.visitField(access, name, desc, signature, value); } @Override @SuppressWarnings("PMD.UseVarargs") //overridden method public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { annotate(); final MethodVisitor originalMethod = super.visitMethod(access, name, desc, signature, exceptions); final Method methd = new Method(name, desc); return new GeneratorAdapter(Privilizer.ASM_VERSION, originalMethod, access, name, desc) { @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { if (Type.getType(Privileged.class).getDescriptor().equals(desc)) { final AccessLevel localAccessLevel = AccessLevel.of(access); if (accessLevel.compareTo(localAccessLevel) > 0) { throw new IllegalStateException(new IllegalAccessException( "Method " + className + "#" + methd + " must have maximum access level '" + accessLevel + "' but is defined wider ('" + localAccessLevel + "')")); } if (AccessLevel.PACKAGE.compareTo(accessLevel) > 0) { privilizer().env.warn("Possible security leak: granting privileges to %s method %s.%s", localAccessLevel, className, methd); } privilegedMethods.put(methd, privilizer().generateName(name)); } return super.visitAnnotation(desc, visible); } @Override public void visitCode() { super.visitCode(); if (!privilegedMethods.containsKey(methd)) { return; } final String impl = privilegedMethods.get(methd); final boolean instanceMethod = !Modifier.isStatic(access); if (policy.isConditional()) { privilizer().env.debug("setting up conditional execution due to policy %s", policy); // test, loading boolean if (policy == Policy.ON_INIT) { getStatic(target, privilizer().generateName("hasSecurityManager"), Type.BOOLEAN_TYPE); } else if (policy == Policy.DYNAMIC) { checkSecurityManager(this); } final Label doPrivileged = new Label(); // if true, goto doPrivileged: ifZCmp(NE, doPrivileged); final Method implMethod = new Method(impl, desc); if (instanceMethod) { loadThis(); loadArgs(); invokeVirtual(target, implMethod); } else { loadArgs(); invokeStatic(target, implMethod); } returnValue(); mark(doPrivileged); } else { privilizer().env.debug("setting up unconditional privileged execution due to policy %s", policy); } // generate action: final Type[] ctorArgs = instanceMethod ? ArrayUtils.insert(0, methd.getArgumentTypes(), target) : methd.getArgumentTypes(); final Type actionType = new ActionGenerator(access, methd, exceptions, PrivilizingVisitor.this) .build(); newInstance(actionType); dup(); if (instanceMethod) { loadThis(); } loadArgs(); invokeConstructor(actionType, new Method("<init>", Type.VOID_TYPE, ctorArgs)); final boolean exc = ArrayUtils.isNotEmpty(exceptions); // mark try if needed final Label privTry = exc ? mark() : null; // execute action final Type arg = exc ? Type.getType(PrivilegedExceptionAction.class) : Type.getType(PrivilegedAction.class); final Method doPrivileged = new Method("doPrivileged", Type.getType(Object.class), new Type[] { arg }); invokeStatic(Type.getType(AccessController.class), doPrivileged); unbox(methd.getReturnType()); returnValue(); if (exc) { final Type caught = Type.getType(PrivilegedActionException.class); // end try final Label privCatch = mark(); // catch catchException(privTry, privCatch, caught); // unwrap invokeVirtual(caught, new Method("getException", Type.getType(Exception.class), Privilizer.EMPTY_TYPE_ARRAY)); // throw throwException(); } // end original method endMethod(); // substitute an impl visitor and continue mv = cv.visitMethod(AccessLevel.PRIVATE.merge(access), impl, desc, signature, exceptions); mv.visitCode(); } }; } @Override public void visitEnd() { annotate(); if (privilizer().policy == Policy.ON_INIT) { final String fieldName = privilizer().generateName("hasSecurityManager"); visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, fieldName, Type.BOOLEAN_TYPE.getDescriptor(), null, null).visitEnd(); final GeneratorAdapter mgen = new GeneratorAdapter(Opcodes.ACC_STATIC, new Method("<clinit>", "()V"), null, Privilizer.EMPTY_TYPE_ARRAY, this); checkSecurityManager(mgen); mgen.putStatic(target, fieldName, Type.BOOLEAN_TYPE); mgen.returnValue(); mgen.endMethod(); } super.visitEnd(); } /** * Generates the instructions to push onto the stack whether there is a * security manager available. * @param mgen to control */ private static void checkSecurityManager(final GeneratorAdapter mgen) { final Label setFalse = new Label(); final Label done = new Label(); mgen.invokeStatic(Type.getType(System.class), new Method("getSecurityManager", Type.getType(SecurityManager.class), Privilizer.EMPTY_TYPE_ARRAY)); mgen.ifNull(setFalse); mgen.push(true); mgen.goTo(done); mgen.mark(setFalse); mgen.push(false); mgen.mark(done); } }