001
014
015 package com.liferay.portal.security.pacl.checker;
016
017 import com.liferay.portal.kernel.configuration.Filter;
018 import com.liferay.portal.kernel.deploy.DeployManagerUtil;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.servlet.ServletContextPool;
022 import com.liferay.portal.kernel.servlet.WebDirDetector;
023 import com.liferay.portal.kernel.servlet.taglib.FileAvailabilityUtil;
024 import com.liferay.portal.kernel.util.ContextPathUtil;
025 import com.liferay.portal.kernel.util.JavaConstants;
026 import com.liferay.portal.kernel.util.PathUtil;
027 import com.liferay.portal.kernel.util.PropsKeys;
028 import com.liferay.portal.kernel.util.ReleaseInfo;
029 import com.liferay.portal.kernel.util.ServerDetector;
030 import com.liferay.portal.kernel.util.StringPool;
031 import com.liferay.portal.kernel.util.StringUtil;
032 import com.liferay.portal.kernel.util.UniqueList;
033 import com.liferay.portal.kernel.util.Validator;
034 import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal;
035 import com.liferay.portal.security.pacl.PACLClassUtil;
036 import com.liferay.portal.servlet.DirectServletRegistryImpl;
037 import com.liferay.portal.util.PropsUtil;
038 import com.liferay.portal.util.PropsValues;
039
040 import java.io.File;
041 import java.io.FilePermission;
042 import java.io.IOException;
043
044 import java.net.URLClassLoader;
045
046 import java.security.Permission;
047
048 import java.util.Iterator;
049 import java.util.List;
050 import java.util.concurrent.CopyOnWriteArrayList;
051
052 import javax.servlet.ServletContext;
053
054 import sun.reflect.Reflection;
055
056
060 public class FileChecker extends BaseChecker {
061
062 public void afterPropertiesSet() {
063 try {
064 _rootDir = WebDirDetector.getRootDir(getClassLoader());
065 }
066 catch (Exception e) {
067
068
069
070 }
071
072 if (_log.isDebugEnabled()) {
073 _log.debug("Root directory " + _rootDir);
074 }
075
076 ServletContext servletContext = ServletContextPool.get(
077 getServletContextName());
078
079 if (servletContext != null) {
080 File tempDir = (File)servletContext.getAttribute(
081 JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
082
083 _workDir = tempDir.getAbsolutePath();
084
085 if (_log.isDebugEnabled()) {
086 _log.debug("Work directory " + _workDir);
087 }
088 }
089
090 _defaultReadPathsFromArray = new String[] {
091 "${auto.deploy.installed.dir}",
092 "${catalina.base}",
093 "${com.sun.aas.instanceRoot}",
094 "${com.sun.aas.installRoot}",
095 "${file.separator}",
096 "${java.io.tmpdir}",
097 "${jboss.home.dir}",
098 "${jetty.home}",
099 "${jonas.base}",
100 "${liferay.web.portal.dir}",
101 "${line.separator}",
102 "${org.apache.geronimo.home.dir}",
103 "${path.separator}",
104 "${plugin.servlet.context.name}",
105 "${release.info.version}",
106 "${resin.home}",
107 "${user.dir}",
108 "${user.home}",
109 "${user.name}",
110 "${weblogic.domain.dir}",
111 "${websphere.profile.dir}",
112 StringPool.DOUBLE_SLASH
113 };
114
115 String installedDir = StringPool.BLANK;
116
117 try {
118 if (DeployManagerUtil.getDeployManager() != null) {
119 installedDir = DeployManagerUtil.getInstalledDir();
120 }
121 }
122 catch (Exception e) {
123 _log.error(e, e);
124 }
125
126 _defaultReadPathsToArray = new String[] {
127 installedDir, System.getProperty("catalina.base"),
128 System.getProperty("com.sun.aas.instanceRoot"),
129 System.getProperty("com.sun.aas.installRoot"),
130 System.getProperty("file.separator"),
131 System.getProperty("java.io.tmpdir"),
132 System.getProperty("jboss.home.dir"),
133 System.getProperty("jetty.home"), System.getProperty("jonas.base"),
134 _portalDir, System.getProperty("line.separator"),
135 System.getProperty("org.apache.geronimo.home.dir"),
136 System.getProperty("path.separator"), getServletContextName(),
137 ReleaseInfo.getVersion(), System.getProperty("resin.home"),
138 System.getProperty("user.dir"), System.getProperty("user.home"),
139 System.getProperty("user.name"), System.getenv("DOMAIN_HOME"),
140 System.getenv("USER_INSTALL_ROOT"), StringPool.SLASH
141 };
142
143 if (_log.isDebugEnabled()) {
144 _log.debug(
145 "Default read paths replace with " +
146 StringUtil.merge(_defaultReadPathsToArray));
147 }
148
149 initPermissions();
150 }
151
152 public void checkPermission(Permission permission) {
153 String name = permission.getName();
154 String actions = permission.getActions();
155
156 if (actions.equals(FILE_PERMISSION_ACTION_DELETE)) {
157 if (!hasDelete(permission)) {
158 throwSecurityException(
159 _log, "Attempted to delete file " + name);
160 }
161 }
162 else if (actions.equals(FILE_PERMISSION_ACTION_EXECUTE)) {
163 if (!hasExecute(permission)) {
164 throwSecurityException(
165 _log, "Attempted to execute file " + name);
166 }
167 }
168 else if (actions.equals(FILE_PERMISSION_ACTION_READ)) {
169 if (PortalSecurityManagerThreadLocal.isCheckReadFile() &&
170 !hasRead(permission)) {
171
172 throwSecurityException(_log, "Attempted to read file " + name);
173 }
174 }
175 else if (actions.equals(FILE_PERMISSION_ACTION_WRITE)) {
176 if (!hasWrite(permission)) {
177 throwSecurityException(_log, "Attempted to write file " + name);
178 }
179 }
180 }
181
182 @Override
183 public AuthorizationProperty generateAuthorizationProperty(
184 Object... arguments) {
185
186 if ((arguments == null) || (arguments.length != 1) ||
187 !(arguments[0] instanceof Permission)) {
188
189 return null;
190 }
191
192 Permission permission = (Permission)arguments[0];
193
194 String actions = permission.getActions();
195
196 String key = null;
197
198 if (actions.equals(FILE_PERMISSION_ACTION_DELETE)) {
199 key = "security-manager-files-delete";
200 }
201 else if (actions.equals(FILE_PERMISSION_ACTION_EXECUTE)) {
202 key = "security-manager-files-execute";
203 }
204 else if (actions.equals(FILE_PERMISSION_ACTION_READ)) {
205 key = "security-manager-files-read";
206 }
207 else if (actions.equals(FILE_PERMISSION_ACTION_WRITE)) {
208 key = "security-manager-files-write";
209 }
210 else {
211 return null;
212 }
213
214 AuthorizationProperty authorizationProperty =
215 new AuthorizationProperty();
216
217 authorizationProperty.setKey(key);
218 authorizationProperty.setValue(permission.getName());
219
220 return authorizationProperty;
221 }
222
223 protected void addCanonicalPath(List<String> paths, String path) {
224 Iterator<String> itr = paths.iterator();
225
226 while (itr.hasNext()) {
227 String curPath = itr.next();
228
229 if (curPath.startsWith(path) &&
230 (curPath.length() > path.length())) {
231
232 itr.remove();
233 }
234 else if (path.startsWith(curPath)) {
235 return;
236 }
237 }
238
239 path = StringUtil.replace(
240 path, StringPool.BACK_SLASH, StringPool.SLASH);
241
242 if (path.endsWith(StringPool.SLASH)) {
243 path = path + "-";
244 }
245
246 paths.add(path);
247 }
248
249 protected void addCanonicalPaths(List<String> paths, File directory)
250 throws IOException {
251
252 addCanonicalPath(
253 paths, directory.getCanonicalPath() + StringPool.SLASH);
254
255 for (File file : directory.listFiles()) {
256 if (file.isDirectory()) {
257 addCanonicalPaths(paths, file);
258 }
259 else {
260 File canonicalFile = new File(file.getCanonicalPath());
261
262 File parentFile = canonicalFile.getParentFile();
263
264 addCanonicalPath(
265 paths, parentFile.getPath() + StringPool.SLASH);
266 }
267 }
268 }
269
270 protected void addDefaultReadPaths(List<String> paths, String selector) {
271 String[] pathsArray = PropsUtil.getArray(
272 PropsKeys.PORTAL_SECURITY_MANAGER_FILE_CHECKER_DEFAULT_READ_PATHS,
273 new Filter(selector));
274
275 for (String path : pathsArray) {
276 path = StringUtil.replace(
277 path, _defaultReadPathsFromArray, _defaultReadPathsToArray);
278
279 paths.add(path);
280 }
281 }
282
283 protected void addPermission(
284 List<Permission> permissions, String path, String actions) {
285
286 if (_log.isDebugEnabled()) {
287 _log.debug("Allowing " + actions + " on " + path);
288 }
289
290 String unixPath = PathUtil.toUnixPath(path);
291
292 Permission unixPermission = new FilePermission(unixPath, actions);
293
294 permissions.add(unixPermission);
295
296 String windowsPath = PathUtil.toWindowsPath(path);
297
298 Permission windowsPermission = new FilePermission(windowsPath, actions);
299
300 permissions.add(windowsPermission);
301 }
302
303 protected List<Permission> getPermissions(String key, String actions) {
304 List<Permission> permissions = new CopyOnWriteArrayList<Permission>();
305
306 String value = getProperty(key);
307
308 if (value != null) {
309 value = StringUtil.replace(
310 value, _defaultReadPathsFromArray, _defaultReadPathsToArray);
311
312 String[] paths = StringUtil.split(value);
313
314 if (value.contains("${comma}")) {
315 for (int i = 0; i < paths.length; i++) {
316 paths[i] = StringUtil.replace(
317 paths[i], "${comma}", StringPool.COMMA);
318 }
319 }
320
321 for (String path : paths) {
322 addPermission(permissions, path, actions);
323 }
324 }
325
326
327
328 String pathContext = ContextPathUtil.getContextPath(
329 PropsValues.PORTAL_CTX);
330
331 ServletContext servletContext = ServletContextPool.get(pathContext);
332
333 if (!actions.equals(FILE_PERMISSION_ACTION_EXECUTE) &&
334 (_workDir != null)) {
335
336 addPermission(permissions, _workDir, actions);
337 addPermission(permissions, _workDir + "/-", actions);
338
339 if (servletContext != null) {
340 File tempDir = (File)servletContext.getAttribute(
341 JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
342
343 String tempDirAbsolutePath = tempDir.getAbsolutePath();
344
345 if (_log.isDebugEnabled()) {
346 _log.debug("Temp directory " + tempDirAbsolutePath);
347 }
348
349 if (actions.equals(FILE_PERMISSION_ACTION_READ)) {
350 addPermission(permissions, tempDirAbsolutePath, actions);
351 }
352
353 addPermission(permissions, tempDirAbsolutePath + "/-", actions);
354 }
355 }
356
357 if (!actions.equals(FILE_PERMISSION_ACTION_READ)) {
358 return permissions;
359 }
360
361 List<String> paths = new UniqueList<String>();
362
363
364
365
366
367
368 try {
369 File file = new File(System.getProperty("java.home") + "/lib");
370
371 addCanonicalPaths(paths, file);
372 }
373 catch (IOException ioe) {
374 _log.error(ioe, ioe);
375 }
376
377
378
379 if (Validator.isNotNull(_globalSharedLibDir)) {
380 paths.add(_globalSharedLibDir + "-");
381 }
382
383
384
385 if (_rootDir != null) {
386 paths.add(_rootDir + "-");
387 }
388
389
390
391 addDefaultReadPaths(paths, ServerDetector.getServerId());
392
393 for (String path : paths) {
394 addPermission(permissions, path, actions);
395 }
396
397 return permissions;
398 }
399
400 protected boolean hasDelete(Permission permission) {
401 for (Permission deleteFilePermission : _deletePermissions) {
402 if (deleteFilePermission.implies(permission)) {
403 return true;
404 }
405 }
406
407 if (ServerDetector.isResin()) {
408 for (int i = 7;; i++) {
409 Class<?> callerClass = Reflection.getCallerClass(i);
410
411 if (callerClass == null) {
412 return false;
413 }
414
415 String callerClassName = callerClass.getName();
416
417 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
418 String actualClassLocation = PACLClassUtil.getClassLocation(
419 callerClass);
420 String expectedClassLocation = PathUtil.toUnixPath(
421 System.getProperty("resin.home") + "/lib/resin.jar!/");
422
423 return actualClassLocation.contains(expectedClassLocation);
424 }
425 }
426 }
427
428 return false;
429 }
430
431 protected boolean hasExecute(Permission permission) {
432 for (Permission executeFilePermission : _executePermissions) {
433 if (executeFilePermission.implies(permission)) {
434 return true;
435 }
436 }
437
438 return false;
439 }
440
441 protected boolean hasRead(Permission permission) {
442 for (Permission readFilePermission : _readPermissions) {
443 if (readFilePermission.implies(permission)) {
444 return true;
445 }
446 }
447
448 if (isJSPCompiler(permission.getName(), FILE_PERMISSION_ACTION_READ)) {
449 return true;
450 }
451
452 for (int i = 7;; i++) {
453 Class<?> callerClass = Reflection.getCallerClass(i);
454
455 if (callerClass == null) {
456 return false;
457 }
458
459 if ((callerClass == DirectServletRegistryImpl.class) ||
460 (callerClass == FileAvailabilityUtil.class)) {
461
462 return true;
463 }
464
465 if (ClassLoader.class.isAssignableFrom(callerClass)) {
466 String callerClassName = callerClass.getName();
467
468 if (!callerClassName.equals(_CLASS_NAME_METHOD_UTIL)) {
469 return true;
470 }
471 }
472
473 if (ServerDetector.isGlassfish()) {
474 Class<?> enclosingClass = callerClass.getEnclosingClass();
475
476 if (enclosingClass != null) {
477 if ((enclosingClass.getEnclosingClass() ==
478 URLClassLoader.class) &&
479 CheckerUtil.isAccessControllerDoPrivileged(i + 1)) {
480
481 return true;
482 }
483 }
484 }
485 else if (ServerDetector.isResin()) {
486 String callerClassName = callerClass.getName();
487
488 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
489 String actualClassLocation = PACLClassUtil.getClassLocation(
490 callerClass);
491 String expectedClassLocation = PathUtil.toUnixPath(
492 System.getProperty("resin.home") + "/lib/resin.jar!/");
493
494 return actualClassLocation.contains(expectedClassLocation);
495 }
496 }
497 }
498 }
499
500 protected boolean hasWrite(Permission permission) {
501 for (Permission writeFilePermission : _writePermissions) {
502 if (writeFilePermission.implies(permission)) {
503 return true;
504 }
505 }
506
507 if (ServerDetector.isResin()) {
508 for (int i = 7;; i++) {
509 Class<?> callerClass = Reflection.getCallerClass(i);
510
511 if (callerClass == null) {
512 return false;
513 }
514
515 String callerClassName = callerClass.getName();
516
517 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
518 String actualClassLocation = PACLClassUtil.getClassLocation(
519 callerClass);
520 String expectedClassLocation = PathUtil.toUnixPath(
521 System.getProperty("resin.home") + "/lib/resin.jar!/");
522
523 return actualClassLocation.contains(expectedClassLocation);
524 }
525 }
526 }
527 else if (ServerDetector.isWebSphere()) {
528 if (isJSPCompiler(
529 permission.getName(), FILE_PERMISSION_ACTION_WRITE)) {
530
531 return true;
532 }
533 }
534
535 return false;
536 }
537
538 protected void initPermissions() {
539 _deletePermissions = getPermissions(
540 "security-manager-files-delete", FILE_PERMISSION_ACTION_DELETE);
541 _executePermissions = getPermissions(
542 "security-manager-files-execute", FILE_PERMISSION_ACTION_EXECUTE);
543 _readPermissions = getPermissions(
544 "security-manager-files-read", FILE_PERMISSION_ACTION_READ);
545 _writePermissions = getPermissions(
546 "security-manager-files-write", FILE_PERMISSION_ACTION_WRITE);
547 }
548
549 private static final String _CLASS_NAME_FILE_PATH =
550 "com.caucho.vfs.FilePath";
551
552 private static final String _CLASS_NAME_METHOD_UTIL =
553 "sun.reflect.misc.MethodUtil";
554
555 private static Log _log = LogFactoryUtil.getLog(FileChecker.class);
556
557 private String[] _defaultReadPathsFromArray;
558 private String[] _defaultReadPathsToArray;
559 private List<Permission> _deletePermissions;
560 private List<Permission> _executePermissions;
561 private String _globalSharedLibDir =
562 PropsValues.LIFERAY_LIB_GLOBAL_SHARED_DIR;
563 private String _portalDir = PropsValues.LIFERAY_WEB_PORTAL_DIR;
564 private List<Permission> _readPermissions;
565 private String _rootDir;
566 private String _workDir;
567 private List<Permission> _writePermissions;
568
569 }