Changhoon Yoon
Committed by Brian O'Connor

ONOS-3521: SM-ONOS performance improvement

Change-Id: I8643187f2ceb35f8e0701d9e7ddb10098f05b244
......@@ -16,26 +16,102 @@
package org.onosproject.security;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import com.google.common.annotations.Beta;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* Aids SM-ONOS to perform API-level permission checking.
*/
@Beta
public final class AppGuard {
private AppGuard() {
}
/**
* Checks if the caller has the required permission only when security-mode is enabled.
*
* @param permission permission to be checked
*/
public static void checkPermission(AppPermission.Type permission) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
System.getSecurityManager().checkPermission(new AppPermission(permission));
if (sm == null) {
return;
}
Object result = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
int contextHash = 0;
AccessControlContext context = AccessController.getContext();
Field f = null;
try {
f = context.getClass().getDeclaredField("context");
f.setAccessible(true);
ProtectionDomain[] domain = (ProtectionDomain[]) f.get(context);
for (ProtectionDomain pd : domain) {
if (pd.getCodeSource() != null) {
contextHash = contextHash ^ pd.getCodeSource().getLocation().hashCode();
} else {
return null;
}
}
return contextHash;
} catch (NoSuchFieldException e) {
return null;
} catch (IllegalAccessException e) {
return null;
}
});
if (result == null) {
sm.checkPermission(new AppPermission(permission));
} else {
AppPermission perm = new AppPermission(permission);
int hash = ((int) result) ^ perm.hashCode();
PermissionCheckCache.getInstance().checkCache(hash, perm);
}
}
private static final class PermissionCheckCache {
private static final Cache<Integer, Boolean> CACHE = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterAccess(10, TimeUnit.MINUTES)
.build();
private PermissionCheckCache() {
}
private static class SingletonHelper {
private static final PermissionCheckCache INSTANCE = new PermissionCheckCache();
}
public static PermissionCheckCache getInstance() {
return SingletonHelper.INSTANCE;
}
public static void checkCache(int key, AppPermission perm) {
try {
CACHE.get(key, () -> {
System.getSecurityManager().checkPermission(perm);
return true;
});
} catch (ExecutionException e) {
System.getSecurityManager().checkPermission(perm);
}
}
}
}
......
......@@ -74,6 +74,4 @@ public interface SecurityAdminService {
* @return Map of list of permissions sorted by permission type
*/
Map<Integer, List<Permission>> getPrintableRequestedPermissions(ApplicationId appId);
}
......
......@@ -54,11 +54,12 @@ import org.onosproject.net.topology.TopologyService;
import org.onosproject.security.SecurityAdminService;
import org.onosproject.store.service.StorageAdminService;
import org.onosproject.store.service.StorageService;
import org.osgi.framework.BundlePermission;
import org.osgi.framework.CapabilityPermission;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.AdaptPermission;
import org.osgi.framework.CapabilityPermission;
import org.osgi.framework.BundlePermission;
import org.osgi.framework.PackagePermission;
import org.osgi.service.cm.ConfigurationPermission;
import javax.net.ssl.SSLPermission;
......@@ -68,6 +69,7 @@ import javax.security.auth.kerberos.DelegationPermission;
import javax.sound.sampled.AudioPermission;
import java.io.FilePermission;
import java.io.SerializablePermission;
import java.lang.reflect.ReflectPermission;
import java.net.NetPermission;
import java.net.SocketPermission;
import java.security.Permissions;
......@@ -159,6 +161,7 @@ public final class DefaultPolicyBuilder {
permSet.add(new PackagePermission("*", PackagePermission.IMPORT));
permSet.add(new AdaptPermission("*", AdaptPermission.ADAPT));
permSet.add(new ConfigurationPermission("*", ConfigurationPermission.CONFIGURE));
permSet.add(new AdminPermission("*", AdminPermission.METADATA));
return permSet;
}
......@@ -359,6 +362,12 @@ public final class DefaultPolicyBuilder {
} else if (permission instanceof ServicePermission) {
return new org.onosproject.security.Permission(
ServicePermission.class.getName(), permission.getName(), permission.getActions());
} else if (permission instanceof AdminPermission) {
return new org.onosproject.security.Permission(
AdminPermission.class.getName(), permission.getName(), permission.getActions());
} else if (permission instanceof ConfigurationPermission) {
return new org.onosproject.security.Permission(
ConfigurationPermission.class.getName(), permission.getName(), permission.getActions());
}
return null;
}
......@@ -416,10 +425,16 @@ public final class DefaultPolicyBuilder {
return new PackagePermission(name, actions);
} else if (ServicePermission.class.getName().equals(classname)) {
return new ServicePermission(name, actions);
} else if (AdminPermission.class.getName().equals(classname)) {
return new AdminPermission(name, actions);
} else if (ConfigurationPermission.class.getName().equals(classname)) {
return new ConfigurationPermission(name, actions);
} else if (ReflectPermission.class.getName().equals(classname)) {
return new ReflectPermission(name, actions);
}
//AllPermission, SecurityPermission, UnresolvedPermission
//AWTPermission, AdminPermission(osgi), ReflectPermission not allowed
//AWTPermission, ReflectPermission not allowed
return null;
}
......@@ -445,4 +460,3 @@ public final class DefaultPolicyBuilder {
return permissions;
}
}
\ No newline at end of file
......
......@@ -92,16 +92,12 @@ public class SecurityModeManager implements SecurityAdminService {
private PermissionAdmin permissionAdmin = getPermissionAdmin();
@Activate
public void activate() {
eventDispatcher.addSink(SecurityModeEvent.class, listenerRegistry);
// add Listeners
logReaderService.addLogListener(securityLogListener);
store.setDelegate(delegate);
if (System.getSecurityManager() == null) {
log.warn("J2EE security manager is disabled.");
deactivate();
......@@ -112,6 +108,7 @@ public class SecurityModeManager implements SecurityAdminService {
deactivate();
return;
}
store.setDelegate(delegate);
log.info("Security-Mode Started");
}
......@@ -302,4 +299,6 @@ public class SecurityModeManager implements SecurityAdminService {
return FrameworkUtil.getBundle(this.getClass()).getBundleContext();
}
}
\ No newline at end of file
......
......@@ -303,14 +303,4 @@ public class DistributedSecurityModeStore
}
return locations;
}
@Override
public void setDelegate(SecurityModeStoreDelegate delegate) {
super.setDelegate(delegate);
}
@Override
public void unsetDelegate(SecurityModeStoreDelegate delegate) {
super.setDelegate(delegate);
}
}
......
......@@ -101,4 +101,5 @@ public interface SecurityModeStore extends Store<SecurityModeEvent, SecurityMode
* @param permissionSet array of PermissionInfo
*/
void acceptPolicy(ApplicationId appId, Set<Permission> permissionSet);
}
\ No newline at end of file
......
......@@ -186,6 +186,7 @@ import org.onosproject.net.resource.link.LinkResourceRequest;
import org.onosproject.net.resource.link.MplsLabel;
import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
import org.onosproject.net.resource.link.MplsLabelResourceRequest;
import org.onosproject.security.Permission;
import org.onosproject.store.Timestamp;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.SetEvent;
......@@ -286,6 +287,7 @@ public final class KryoNamespaces {
ApplicationState.class,
ApplicationRole.class,
DefaultApplication.class,
Permission.class,
Device.Type.class,
Port.Type.class,
ChassisId.class,
......