001package org.intellimate.izou.security; 002 003import org.intellimate.izou.addon.AddOnModel; 004import org.intellimate.izou.main.Main; 005import org.intellimate.izou.security.exceptions.IzouPermissionException; 006 007import java.io.File; 008import java.io.FilePermission; 009import java.io.IOException; 010import java.net.URL; 011import java.security.Permission; 012import java.util.ArrayList; 013import java.util.List; 014 015/** 016 * The FilePermissionModule is used to check the access to files 017 * @author LeanderK 018 * @version 1.0 019 */ 020public class FilePermissionModule extends PermissionModule { 021 private final List<String> forbiddenReadFiles; 022 private final List<File> allowedWriteDirectories; 023 private final List<File> forbiddenWriteDirectories; 024 private final List<String> forbiddenWriteFilesNames; 025 026 /** 027 * Creates a new PermissionModule 028 * 029 * @param main the instance of main 030 */ 031 FilePermissionModule(Main main, SecurityManager securityManager) { 032 super(main, securityManager); 033 forbiddenReadFiles = new ArrayList<>(); 034 forbiddenWriteDirectories = new ArrayList<>(); 035 forbiddenWriteFilesNames = new ArrayList<>(); 036 forbiddenWriteFilesNames.add("addon_config.properties"); 037 allowedWriteDirectories = new ArrayList<>(); 038 allowedWriteDirectories.add(main.getFileSystemManager().getResourceLocation()); 039 allowedWriteDirectories.add(main.getFileSystemManager().getLogsLocation()); 040 allowedWriteDirectories.add(main.getFileSystemManager().getPropertiesLocation()); 041 allowedWriteDirectories.add(main.getFileSystemManager().getResourceLocation()); 042 allowedWriteDirectories.add(main.getFileSystemManager().getLibLocation()); 043 if (getMain().getFileSystemManager().getIzouJarLocation().isDirectory()) { 044 allowedWriteDirectories.add(new File(getMain().getFileSystemManager().getIzouJarLocation() + File.separator + "META-INF" + File.separator + "services")); 045 } else { 046 URL metaInfo = this.getClass().getClassLoader().getResource("META-INF/services"); 047 if (metaInfo != null) 048 allowedWriteDirectories.add(new File(metaInfo.getFile())); 049 } 050 051 if (Boolean.getBoolean("debug")) { 052 allowedWriteDirectories.add(new File(System.getProperty("user.home") + File.separator + ".m2")); 053 allowedWriteDirectories.add(main.getFileSystemManager().getIzouJarLocation()); 054 allowedWriteDirectories.add(new File(System.getProperty("java.home"))); 055 } 056 } 057 058 /** 059 * returns true if able to check permissions 060 * 061 * @param permission the permission to check 062 * @return true if able to, false if not 063 */ 064 @Override 065 public boolean canCheckPermission(Permission permission) { 066 return permission instanceof FilePermission; 067 } 068 069 /** 070 * Checks if the given addOn is allowed to access the requested service and registers them if not yet registered. 071 * 072 * @param permission the Permission to check 073 * @param addon the identifiable to check 074 * @throws IzouPermissionException thrown if the addOn is not allowed to access its requested service 075 */ 076 @Override 077 public void checkPermission(Permission permission, AddOnModel addon) throws IzouPermissionException { 078 String canonicalName = permission.getName().intern().toLowerCase(); 079 String canonicalAction = permission.getActions().intern().toLowerCase(); 080 081 if (canonicalName.contains("all files") || canonicalAction.equals("execute")) { 082 throw getException(permission.getName()); 083 } 084 if (canonicalAction.equals("read")) { 085 fileReadCheck(canonicalName); 086 } 087 fileWriteCheck(canonicalName, addon); 088 } 089 090 /** 091 * Determines if the file at the given file path is safe to read from in all aspects, if so returns true, else false 092 * 093 * @param filePath the path to the file to read from 094 */ 095 void fileReadCheck(String filePath) { 096 File potentialFile = new File(filePath); 097 String canonicalPath; 098 try { 099 canonicalPath = potentialFile.getCanonicalPath(); 100 } catch (IOException e) { 101 error("Error getting canonical path", e); 102 throw getException(filePath); 103 } 104 105 if (forbiddenReadFiles.stream().anyMatch(canonicalPath::startsWith)) { 106 throw getException(filePath); 107 } 108 } 109 110 /** 111 * Determines if the file at the given file path is safe to write to in all aspects, if so returns true, else false 112 * 113 * @param filePath the path to the file to write to 114 * @param addOnModel the AddonModel 115 */ 116 void fileWriteCheck(String filePath, AddOnModel addOnModel) { 117 File request; 118 try { 119 request = new File(filePath).getCanonicalFile(); 120 } catch (IOException e) { 121 error("Error getting canonical path", e); 122 throw getException(filePath); 123 } 124 125 isForbidden(request, addOnModel); 126 127 boolean success = false; 128 if (allowedWriteDirectories.stream() 129 .anyMatch(compare -> request.toPath().startsWith(compare.toPath()))) { 130 success = true; 131 } 132 133 for (String name : forbiddenWriteFilesNames) { 134 if (request.getName().equals(name)) { 135 success = false; 136 } 137 } 138 139 if (!success) { 140 throw getException(filePath); 141 } 142 143 if (!getSecurityManager().getSecureAccess().checkForExistingFileOrDirectory(request.toString()) 144 || getSecurityManager().getSecureAccess().checkForDirectory(request.toString())) { 145 return; 146 } 147 } 148 149 /** 150 * throws an Exception if the 151 * @param request the requested File 152 * @param addOnModel the AddonModel 153 */ 154 private void isForbidden(File request, AddOnModel addOnModel) { 155 if (forbiddenWriteDirectories.stream() 156 .anyMatch(compare -> request.toPath().startsWith(compare.toPath()))) { 157 throw getException("file: " + request.toString() + " is forbidden. Attempt made by: " 158 + addOnModel.getID()); 159 } 160 } 161}