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 protected void addCanonicalPath(List<String> paths, String path) {
183 Iterator<String> itr = paths.iterator();
184
185 while (itr.hasNext()) {
186 String curPath = itr.next();
187
188 if (curPath.startsWith(path) &&
189 (curPath.length() > path.length())) {
190
191 itr.remove();
192 }
193 else if (path.startsWith(curPath)) {
194 return;
195 }
196 }
197
198 path = StringUtil.replace(
199 path, StringPool.BACK_SLASH, StringPool.SLASH);
200
201 if (path.endsWith(StringPool.SLASH)) {
202 path = path + "-";
203 }
204
205 paths.add(path);
206 }
207
208 protected void addCanonicalPaths(List<String> paths, File directory)
209 throws IOException {
210
211 addCanonicalPath(
212 paths, directory.getCanonicalPath() + StringPool.SLASH);
213
214 for (File file : directory.listFiles()) {
215 if (file.isDirectory()) {
216 addCanonicalPaths(paths, file);
217 }
218 else {
219 File canonicalFile = new File(file.getCanonicalPath());
220
221 File parentFile = canonicalFile.getParentFile();
222
223 addCanonicalPath(
224 paths, parentFile.getPath() + StringPool.SLASH);
225 }
226 }
227 }
228
229 protected void addDefaultReadPaths(List<String> paths, String selector) {
230 String[] pathsArray = PropsUtil.getArray(
231 PropsKeys.PORTAL_SECURITY_MANAGER_FILE_CHECKER_DEFAULT_READ_PATHS,
232 new Filter(selector));
233
234 for (String path : pathsArray) {
235 path = StringUtil.replace(
236 path, _defaultReadPathsFromArray, _defaultReadPathsToArray);
237
238 paths.add(path);
239 }
240 }
241
242 protected void addPermission(
243 List<Permission> permissions, String path, String actions) {
244
245 if (_log.isDebugEnabled()) {
246 _log.debug("Allowing " + actions + " on " + path);
247 }
248
249 String unixPath = PathUtil.toUnixPath(path);
250
251 Permission unixPermission = new FilePermission(unixPath, actions);
252
253 permissions.add(unixPermission);
254
255 String windowsPath = PathUtil.toWindowsPath(path);
256
257 Permission windowsPermission = new FilePermission(windowsPath, actions);
258
259 permissions.add(windowsPermission);
260 }
261
262 protected List<Permission> getPermissions(String key, String actions) {
263 List<Permission> permissions = new CopyOnWriteArrayList<Permission>();
264
265 String value = getProperty(key);
266
267 if (value != null) {
268 value = StringUtil.replace(
269 value, _defaultReadPathsFromArray, _defaultReadPathsToArray);
270
271 String[] paths = StringUtil.split(value);
272
273 if (value.contains("${comma}")) {
274 for (int i = 0; i < paths.length; i++) {
275 paths[i] = StringUtil.replace(
276 paths[i], "${comma}", StringPool.COMMA);
277 }
278 }
279
280 for (String path : paths) {
281 addPermission(permissions, path, actions);
282 }
283 }
284
285
286
287 String pathContext = ContextPathUtil.getContextPath(
288 PropsValues.PORTAL_CTX);
289
290 ServletContext servletContext = ServletContextPool.get(pathContext);
291
292 if (!actions.equals(FILE_PERMISSION_ACTION_EXECUTE) &&
293 (_workDir != null)) {
294
295 addPermission(permissions, _workDir, actions);
296 addPermission(permissions, _workDir + "/-", actions);
297
298 if (servletContext != null) {
299 File tempDir = (File)servletContext.getAttribute(
300 JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
301
302 String tempDirAbsolutePath = tempDir.getAbsolutePath();
303
304 if (_log.isDebugEnabled()) {
305 _log.debug("Temp directory " + tempDirAbsolutePath);
306 }
307
308 if (actions.equals(FILE_PERMISSION_ACTION_READ)) {
309 addPermission(permissions, tempDirAbsolutePath, actions);
310 }
311
312 addPermission(permissions, tempDirAbsolutePath + "/-", actions);
313 }
314 }
315
316 if (!actions.equals(FILE_PERMISSION_ACTION_READ)) {
317 return permissions;
318 }
319
320 List<String> paths = new UniqueList<String>();
321
322
323
324
325
326
327 try {
328 File file = new File(System.getProperty("java.home") + "/lib");
329
330 addCanonicalPaths(paths, file);
331 }
332 catch (IOException ioe) {
333 _log.error(ioe, ioe);
334 }
335
336
337
338 if (Validator.isNotNull(_globalSharedLibDir)) {
339 paths.add(_globalSharedLibDir + "-");
340 }
341
342
343
344 if (_rootDir != null) {
345 paths.add(_rootDir + "-");
346 }
347
348
349
350 addDefaultReadPaths(paths, ServerDetector.getServerId());
351
352 for (String path : paths) {
353 addPermission(permissions, path, actions);
354 }
355
356 return permissions;
357 }
358
359 protected boolean hasDelete(Permission permission) {
360 for (Permission deleteFilePermission : _deletePermissions) {
361 if (deleteFilePermission.implies(permission)) {
362 return true;
363 }
364 }
365
366 if (ServerDetector.isResin()) {
367 for (int i = 7;; i++) {
368 Class<?> callerClass = Reflection.getCallerClass(i);
369
370 if (callerClass == null) {
371 return false;
372 }
373
374 String callerClassName = callerClass.getName();
375
376 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
377 String actualClassLocation = PACLClassUtil.getClassLocation(
378 callerClass);
379 String expectedClassLocation = PathUtil.toUnixPath(
380 System.getProperty("resin.home") + "/lib/resin.jar!/");
381
382 return actualClassLocation.contains(expectedClassLocation);
383 }
384 }
385 }
386
387 return false;
388 }
389
390 protected boolean hasExecute(Permission permission) {
391 for (Permission executeFilePermission : _executePermissions) {
392 if (executeFilePermission.implies(permission)) {
393 return true;
394 }
395 }
396
397 return false;
398 }
399
400 protected boolean hasRead(Permission permission) {
401 for (Permission readFilePermission : _readPermissions) {
402 if (readFilePermission.implies(permission)) {
403 return true;
404 }
405 }
406
407 if (isJSPCompiler(permission.getName(), FILE_PERMISSION_ACTION_READ)) {
408 return true;
409 }
410
411 for (int i = 7;; i++) {
412 Class<?> callerClass = Reflection.getCallerClass(i);
413
414 if (callerClass == null) {
415 return false;
416 }
417
418 if ((callerClass == DirectServletRegistryImpl.class) ||
419 (callerClass == FileAvailabilityUtil.class)) {
420
421 return true;
422 }
423
424 if (ClassLoader.class.isAssignableFrom(callerClass)) {
425 String callerClassName = callerClass.getName();
426
427 if (!callerClassName.equals(_CLASS_NAME_METHOD_UTIL)) {
428 return true;
429 }
430 }
431
432 if (ServerDetector.isGlassfish()) {
433 Class<?> enclosingClass = callerClass.getEnclosingClass();
434
435 if (enclosingClass != null) {
436 if ((enclosingClass.getEnclosingClass() ==
437 URLClassLoader.class) &&
438 CheckerUtil.isAccessControllerDoPrivileged(i + 1)) {
439
440 return true;
441 }
442 }
443 }
444 else if (ServerDetector.isResin()) {
445 String callerClassName = callerClass.getName();
446
447 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
448 String actualClassLocation = PACLClassUtil.getClassLocation(
449 callerClass);
450 String expectedClassLocation = PathUtil.toUnixPath(
451 System.getProperty("resin.home") + "/lib/resin.jar!/");
452
453 return actualClassLocation.contains(expectedClassLocation);
454 }
455 }
456 }
457 }
458
459 protected boolean hasWrite(Permission permission) {
460 for (Permission writeFilePermission : _writePermissions) {
461 if (writeFilePermission.implies(permission)) {
462 return true;
463 }
464 }
465
466 if (ServerDetector.isResin()) {
467 for (int i = 7;; i++) {
468 Class<?> callerClass = Reflection.getCallerClass(i);
469
470 if (callerClass == null) {
471 return false;
472 }
473
474 String callerClassName = callerClass.getName();
475
476 if (callerClassName.equals(_CLASS_NAME_FILE_PATH)) {
477 String actualClassLocation = PACLClassUtil.getClassLocation(
478 callerClass);
479 String expectedClassLocation = PathUtil.toUnixPath(
480 System.getProperty("resin.home") + "/lib/resin.jar!/");
481
482 return actualClassLocation.contains(expectedClassLocation);
483 }
484 }
485 }
486 else if (ServerDetector.isWebSphere()) {
487 if (isJSPCompiler(
488 permission.getName(), FILE_PERMISSION_ACTION_WRITE)) {
489
490 return true;
491 }
492 }
493
494 return false;
495 }
496
497 protected void initPermissions() {
498 _deletePermissions = getPermissions(
499 "security-manager-files-delete", FILE_PERMISSION_ACTION_DELETE);
500 _executePermissions = getPermissions(
501 "security-manager-files-execute", FILE_PERMISSION_ACTION_EXECUTE);
502 _readPermissions = getPermissions(
503 "security-manager-files-read", FILE_PERMISSION_ACTION_READ);
504 _writePermissions = getPermissions(
505 "security-manager-files-write", FILE_PERMISSION_ACTION_WRITE);
506 }
507
508 private static final String _CLASS_NAME_FILE_PATH =
509 "com.caucho.vfs.FilePath";
510
511 private static final String _CLASS_NAME_METHOD_UTIL =
512 "sun.reflect.misc.MethodUtil";
513
514 private static Log _log = LogFactoryUtil.getLog(FileChecker.class);
515
516 private String[] _defaultReadPathsFromArray;
517 private String[] _defaultReadPathsToArray;
518 private List<Permission> _deletePermissions;
519 private List<Permission> _executePermissions;
520 private String _globalSharedLibDir =
521 PropsValues.LIFERAY_LIB_GLOBAL_SHARED_DIR;
522 private String _portalDir = PropsValues.LIFERAY_WEB_PORTAL_DIR;
523 private List<Permission> _readPermissions;
524 private String _rootDir;
525 private String _workDir;
526 private List<Permission> _writePermissions;
527
528 }