blob: 021ff00cc654749d48c24c66ecfeb1063d55039a [file] [log] [blame]
Changhoon Yoon23dee8f2015-05-18 22:19:49 +09001package org.onosproject.security.impl;
2
Changhoon Yoon23dee8f2015-05-18 22:19:49 +09003import org.apache.felix.scr.annotations.Component;
4import org.apache.felix.scr.annotations.Reference;
5import org.apache.felix.scr.annotations.ReferenceCardinality;
6import org.apache.felix.scr.annotations.Activate;
7import org.apache.felix.scr.annotations.Deactivate;
8import org.apache.karaf.features.BundleInfo;
9import org.apache.karaf.features.Feature;
10import org.apache.karaf.features.FeaturesService;
11
12import org.onosproject.app.ApplicationAdminService;
13import org.onosproject.app.ApplicationEvent;
14import org.onosproject.app.ApplicationListener;
15import org.onosproject.app.ApplicationState;
16import org.onosproject.core.Application;
17import org.onosproject.core.ApplicationId;
18import org.onosproject.core.Permission;
Changhoon Yoon541ef712015-05-23 17:18:34 +090019import org.onosproject.security.AppPermission;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090020import org.osgi.framework.Bundle;
21import org.osgi.framework.BundleContext;
22import org.osgi.framework.BundleEvent;
23import org.osgi.framework.BundleListener;
24import org.osgi.framework.FrameworkUtil;
25import org.osgi.framework.PackagePermission;
26import org.osgi.framework.ServicePermission;
27import org.osgi.service.log.LogEntry;
28import org.osgi.service.log.LogListener;
29import org.osgi.service.log.LogReaderService;
30import org.osgi.service.permissionadmin.PermissionInfo;
31
32import java.security.AccessControlException;
33import java.security.AllPermission;
34import java.util.ArrayList;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090035import java.util.List;
Sho SHIMIZU6cd33302015-06-30 19:09:07 -070036import java.util.Map;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090037import java.util.Set;
Sho SHIMIZU6cd33302015-06-30 19:09:07 -070038import java.util.concurrent.ConcurrentHashMap;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090039import java.util.stream.Collectors;
40
41import org.osgi.service.permissionadmin.PermissionAdmin;
42import org.slf4j.Logger;
43
44import static org.slf4j.LoggerFactory.getLogger;
45
46/**
47 * Security-Mode ONOS management implementation.
48 */
49
50//TODO : implement a dedicated distributed store for SM-ONOS
51
52@Component(immediate = true)
53public class SecurityModeManager {
54
55 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
56 protected ApplicationAdminService appAdminService;
57
58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 protected FeaturesService featuresService;
60
61 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
62 protected LogReaderService logReaderService;
63
64 private final Logger log = getLogger(getClass());
65
66 private SecurityBundleListener securityBundleListener = new SecurityBundleListener();
67
68 private SecurityApplicationListener securityApplicationListener = new SecurityApplicationListener();
69
70 private SecurityLogListener securityLogListener = new SecurityLogListener();
71
72 private Bundle bundle = null;
73
74 private BundleContext bundleContext = null;
75
76 private PermissionAdmin permissionAdmin = null;
77
Sho SHIMIZU6cd33302015-06-30 19:09:07 -070078 private Map<String, ApplicationId> appTracker = null;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090079
Sho SHIMIZU6cd33302015-06-30 19:09:07 -070080 private Map<Permission, Set<String>> serviceDirectory = null;
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090081
82
83 @Activate
84 public void activate() {
85 if (System.getSecurityManager() == null) {
86 log.warn("J2EE security manager is disabled.");
87 deactivate();
88 return;
89 }
90 bundle = FrameworkUtil.getBundle(this.getClass());
91 bundleContext = bundle.getBundleContext();
92
93 bundleContext.addBundleListener(securityBundleListener);
94 appAdminService.addListener(securityApplicationListener);
95 logReaderService.addLogListener(securityLogListener);
Sho SHIMIZU6cd33302015-06-30 19:09:07 -070096 appTracker = new ConcurrentHashMap<>();
Changhoon Yoon23dee8f2015-05-18 22:19:49 +090097
98 permissionAdmin = getPermissionAdmin(bundleContext);
99 if (permissionAdmin == null) {
100 log.warn("Permission Admin not found.");
101 this.deactivate();
102 return;
103 }
104
105 serviceDirectory = PolicyBuilder.getServiceDirectory();
106
107 PermissionInfo[] allPerm = {
108 new PermissionInfo(AllPermission.class.getName(), "", ""), };
109
110 permissionAdmin.setPermissions(bundle.getLocation(), allPerm);
111 log.warn("Security-Mode Started");
Changhoon Yoon23dee8f2015-05-18 22:19:49 +0900112 }
113
114
115 @Deactivate
116 public void deactivate() {
117 bundleContext.removeBundleListener(securityBundleListener);
118 appAdminService.removeListener(securityApplicationListener);
119 logReaderService.removeLogListener(securityLogListener);
120 log.info("Stopped");
121
122 }
123
124 private class SecurityApplicationListener implements ApplicationListener {
125
126 @Override
127 public void event(ApplicationEvent event) {
128 //App needs to be restarted
129 if (event.type() == ApplicationEvent.Type.APP_PERMISSIONS_CHANGED) {
130 if (appAdminService.getState(event.subject().id()) == ApplicationState.ACTIVE) {
131 appAdminService.deactivate(event.subject().id());
132 print("Permissions updated (%s). Deactivating...",
133 event.subject().id().name());
134 }
135 }
136 }
137 }
138
139 private class SecurityBundleListener implements BundleListener {
140
141 @Override
142 public void bundleChanged(BundleEvent event) {
143 switch (event.getType()) {
144 case BundleEvent.INSTALLED:
145 setPermissions(event);
146 break;
147 case BundleEvent.UNINSTALLED:
148 clearPermissions(event);
149 break;
150 default:
151 break;
152 }
153 }
154 }
155
156 private void clearPermissions(BundleEvent bundleEvent) {
157 if (appTracker.containsKey(bundleEvent.getBundle().getLocation())) {
158 permissionAdmin.setPermissions(bundleEvent.getBundle().getLocation(), new PermissionInfo[]{});
159 appTracker.remove(bundleEvent.getBundle().getLocation());
160 }
161 }
162
163 // find the location of the installed bundle and enforce policy
164 private void setPermissions(BundleEvent bundleEvent) {
165 for (Application app : appAdminService.getApplications()) {
166 if (getBundleLocations(app).contains(bundleEvent.getBundle().getLocation())) {
167 String location = bundleEvent.getBundle().getLocation();
168
169 Set<org.onosproject.core.Permission> permissions =
170 appAdminService.getPermissions(app.id());
171
172 //Permissions granted by user overrides the permissions specified in App.Xml file
173 if (permissions == null) {
174 permissions = app.permissions();
175 }
176
177 if (permissions.isEmpty()) {
178 print("Application %s has not been granted any permission.", app.id().name());
179 }
180
181 PermissionInfo[] perms = null;
182
183 switch (app.role()) {
184 case ADMIN:
185 perms = PolicyBuilder.getAdminApplicationPermissions(serviceDirectory);
186 break;
187 case REGULAR:
188 perms = PolicyBuilder.getApplicationPermissions(serviceDirectory, permissions);
189 break;
190 case UNSPECIFIED:
191 default:
192 //no role has been assigned.
193 perms = PolicyBuilder.getDefaultPerms();
194 log.warn("Application %s has no role assigned.", app.id().name());
195 break;
196 }
197 permissionAdmin.setPermissions(location, perms);
198 appTracker.put(location, app.id());
199 break;
200 }
201 }
202 }
203
204 //TODO: dispatch security policy violation event via distributed store
205 //immediately notify and deactivate the application upon policy violation
206 private class SecurityLogListener implements LogListener {
207 @Override
208 public void logged(LogEntry entry) {
209 if (entry != null) {
210 if (entry.getException() != null) {
211 ApplicationId applicationId = appTracker.get(entry.getBundle().getLocation());
212 if (applicationId != null) {
213 if (appAdminService.getState(applicationId).equals(ApplicationState.ACTIVE)) {
214 if (entry.getException() instanceof AccessControlException) {
215 java.security.Permission permission =
216 ((AccessControlException) entry.getException()).getPermission();
217 handleException(applicationId.name(), permission);
218 appAdminService.deactivate(applicationId);
219 }
220 }
221 }
222 }
223 }
224 }
225 }
226
227 private void handleException(String name, java.security.Permission perm) {
228 if (perm instanceof ServicePermission || perm instanceof PackagePermission) {
229 print("%s has attempted to %s %s.", name, perm.getActions(), perm.getName());
230 } else if (perm instanceof AppPermission) {
231 print("%s has attempted to call an NB API that requires %s permission.",
232 name, perm.getName().toUpperCase());
233 } else {
234 print("%s has attempted to perform an action that requires %s", name, perm.toString());
235 }
236 print("POLICY VIOLATION: Deactivating %s.", name);
237
238 }
239 private void print(String format, Object... args) {
240 System.out.println(String.format("SM-ONOS: " + format, args));
241 log.warn(String.format(format, args));
242 }
243
244 private List<String> getBundleLocations(Application app) {
245 List<String> locations = new ArrayList();
246 for (String name : app.features()) {
247 try {
248 Feature feature = featuresService.getFeature(name);
249 locations.addAll(
250 feature.getBundles().stream().map(BundleInfo::getLocation).collect(Collectors.toList()));
251 } catch (Exception e) {
252 return locations;
253 }
254 }
255 return locations;
256 }
257
258 private PermissionAdmin getPermissionAdmin(BundleContext context) {
259 return (PermissionAdmin) context.getService(context.getServiceReference(PermissionAdmin.class.getName()));
260 }
261
262}