blob: bda171e927b84ae9813b2b7d8f145c9eaca469d3 [file] [log] [blame]
/*
* 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);
}
}