| package org.onosproject.security.impl; |
| |
| import org.apache.commons.collections.FastHashMap; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Reference; |
| import org.apache.felix.scr.annotations.ReferenceCardinality; |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Deactivate; |
| import org.apache.karaf.features.BundleInfo; |
| import org.apache.karaf.features.Feature; |
| import org.apache.karaf.features.FeaturesService; |
| |
| import org.onosproject.app.ApplicationAdminService; |
| import org.onosproject.app.ApplicationEvent; |
| import org.onosproject.app.ApplicationListener; |
| import org.onosproject.app.ApplicationState; |
| import org.onosproject.core.Application; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.Permission; |
| import org.onosproject.security.util.AppPermission; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.PackagePermission; |
| import org.osgi.framework.ServicePermission; |
| import org.osgi.service.log.LogEntry; |
| import org.osgi.service.log.LogListener; |
| import org.osgi.service.log.LogReaderService; |
| import org.osgi.service.permissionadmin.PermissionInfo; |
| |
| import java.security.AccessControlException; |
| import java.security.AllPermission; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import org.osgi.service.permissionadmin.PermissionAdmin; |
| import org.slf4j.Logger; |
| |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Security-Mode ONOS management implementation. |
| */ |
| |
| //TODO : implement a dedicated distributed store for SM-ONOS |
| |
| @Component(immediate = true) |
| public class SecurityModeManager { |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected ApplicationAdminService appAdminService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected FeaturesService featuresService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected LogReaderService logReaderService; |
| |
| private final Logger log = getLogger(getClass()); |
| |
| private SecurityBundleListener securityBundleListener = new SecurityBundleListener(); |
| |
| private SecurityApplicationListener securityApplicationListener = new SecurityApplicationListener(); |
| |
| private SecurityLogListener securityLogListener = new SecurityLogListener(); |
| |
| private Bundle bundle = null; |
| |
| private BundleContext bundleContext = null; |
| |
| private PermissionAdmin permissionAdmin = null; |
| |
| private HashMap<String, ApplicationId> appTracker = null; |
| |
| private HashMap<Permission, Set<String>> serviceDirectory = null; |
| |
| |
| @Activate |
| public void activate() { |
| if (System.getSecurityManager() == null) { |
| log.warn("J2EE security manager is disabled."); |
| deactivate(); |
| return; |
| } |
| bundle = FrameworkUtil.getBundle(this.getClass()); |
| bundleContext = bundle.getBundleContext(); |
| |
| bundleContext.addBundleListener(securityBundleListener); |
| appAdminService.addListener(securityApplicationListener); |
| logReaderService.addLogListener(securityLogListener); |
| appTracker = new FastHashMap(); |
| |
| permissionAdmin = getPermissionAdmin(bundleContext); |
| if (permissionAdmin == null) { |
| log.warn("Permission Admin not found."); |
| this.deactivate(); |
| return; |
| } |
| |
| serviceDirectory = PolicyBuilder.getServiceDirectory(); |
| |
| PermissionInfo[] allPerm = { |
| new PermissionInfo(AllPermission.class.getName(), "", ""), }; |
| |
| permissionAdmin.setPermissions(bundle.getLocation(), allPerm); |
| log.warn("Security-Mode Started"); |
| |
| } |
| |
| |
| @Deactivate |
| public void deactivate() { |
| bundleContext.removeBundleListener(securityBundleListener); |
| appAdminService.removeListener(securityApplicationListener); |
| logReaderService.removeLogListener(securityLogListener); |
| log.info("Stopped"); |
| |
| } |
| |
| private class SecurityApplicationListener implements ApplicationListener { |
| |
| @Override |
| public void event(ApplicationEvent event) { |
| //App needs to be restarted |
| if (event.type() == ApplicationEvent.Type.APP_PERMISSIONS_CHANGED) { |
| if (appAdminService.getState(event.subject().id()) == ApplicationState.ACTIVE) { |
| appAdminService.deactivate(event.subject().id()); |
| print("Permissions updated (%s). Deactivating...", |
| event.subject().id().name()); |
| } |
| } |
| } |
| } |
| |
| private class SecurityBundleListener implements BundleListener { |
| |
| @Override |
| public void bundleChanged(BundleEvent event) { |
| switch (event.getType()) { |
| case BundleEvent.INSTALLED: |
| setPermissions(event); |
| break; |
| case BundleEvent.UNINSTALLED: |
| clearPermissions(event); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| private void clearPermissions(BundleEvent bundleEvent) { |
| if (appTracker.containsKey(bundleEvent.getBundle().getLocation())) { |
| permissionAdmin.setPermissions(bundleEvent.getBundle().getLocation(), new PermissionInfo[]{}); |
| appTracker.remove(bundleEvent.getBundle().getLocation()); |
| } |
| } |
| |
| // find the location of the installed bundle and enforce policy |
| private void setPermissions(BundleEvent bundleEvent) { |
| for (Application app : appAdminService.getApplications()) { |
| if (getBundleLocations(app).contains(bundleEvent.getBundle().getLocation())) { |
| String location = bundleEvent.getBundle().getLocation(); |
| |
| Set<org.onosproject.core.Permission> permissions = |
| appAdminService.getPermissions(app.id()); |
| |
| //Permissions granted by user overrides the permissions specified in App.Xml file |
| if (permissions == null) { |
| permissions = app.permissions(); |
| } |
| |
| if (permissions.isEmpty()) { |
| print("Application %s has not been granted any permission.", app.id().name()); |
| } |
| |
| PermissionInfo[] perms = null; |
| |
| switch (app.role()) { |
| case ADMIN: |
| perms = PolicyBuilder.getAdminApplicationPermissions(serviceDirectory); |
| break; |
| case REGULAR: |
| perms = PolicyBuilder.getApplicationPermissions(serviceDirectory, permissions); |
| break; |
| case UNSPECIFIED: |
| default: |
| //no role has been assigned. |
| perms = PolicyBuilder.getDefaultPerms(); |
| log.warn("Application %s has no role assigned.", app.id().name()); |
| break; |
| } |
| permissionAdmin.setPermissions(location, perms); |
| appTracker.put(location, app.id()); |
| break; |
| } |
| } |
| } |
| |
| //TODO: dispatch security policy violation event via distributed store |
| //immediately notify and deactivate the application upon policy violation |
| private class SecurityLogListener implements LogListener { |
| @Override |
| public void logged(LogEntry entry) { |
| if (entry != null) { |
| if (entry.getException() != null) { |
| ApplicationId applicationId = appTracker.get(entry.getBundle().getLocation()); |
| if (applicationId != null) { |
| if (appAdminService.getState(applicationId).equals(ApplicationState.ACTIVE)) { |
| if (entry.getException() instanceof AccessControlException) { |
| java.security.Permission permission = |
| ((AccessControlException) entry.getException()).getPermission(); |
| handleException(applicationId.name(), permission); |
| appAdminService.deactivate(applicationId); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void handleException(String name, java.security.Permission perm) { |
| if (perm instanceof ServicePermission || perm instanceof PackagePermission) { |
| print("%s has attempted to %s %s.", name, perm.getActions(), perm.getName()); |
| } else if (perm instanceof AppPermission) { |
| print("%s has attempted to call an NB API that requires %s permission.", |
| name, perm.getName().toUpperCase()); |
| } else { |
| print("%s has attempted to perform an action that requires %s", name, perm.toString()); |
| } |
| print("POLICY VIOLATION: Deactivating %s.", name); |
| |
| } |
| private void print(String format, Object... args) { |
| System.out.println(String.format("SM-ONOS: " + format, args)); |
| log.warn(String.format(format, args)); |
| } |
| |
| private List<String> getBundleLocations(Application app) { |
| List<String> locations = new ArrayList(); |
| for (String name : app.features()) { |
| try { |
| Feature feature = featuresService.getFeature(name); |
| locations.addAll( |
| feature.getBundles().stream().map(BundleInfo::getLocation).collect(Collectors.toList())); |
| } catch (Exception e) { |
| return locations; |
| } |
| } |
| return locations; |
| } |
| |
| private PermissionAdmin getPermissionAdmin(BundleContext context) { |
| return (PermissionAdmin) context.getService(context.getServiceReference(PermissionAdmin.class.getName())); |
| } |
| |
| } |