/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.aliyun.odps.local.common.security;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Policy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.aliyun.odps.Odps;
import com.aliyun.odps.local.common.Constants;
import com.aliyun.odps.local.common.WareHouse;
import com.aliyun.odps.utils.ReflectionUtils;
public class SecurityClient {
private static String lineSeperator = System.getProperty("line.separator", "\n");
private static volatile SecurityClient securityClient;
private String policyFilePath;
// user parameters
private boolean isSecurityEnabled;
private boolean isJNIEnabled; // is jni enabled
private String userDefinePolicy;
private SecurityManager oldSecurityManager;
private Policy oldPolicy;
// MR,UDF
private ApplicatitionType appType;
private List<String> appCodeBase;
private Map<String, String> replacement; // replace module in policy file, like
// $TEMP_HOME$,$WAREHOUSE_HOME$
private SecurityClient() {}
private static synchronized SecurityClient getInstance() {
if (securityClient == null) {
securityClient = new SecurityClient();
}
return securityClient;
}
public static void init(ApplicatitionType appType, List<String> appCodeBase,
Map<String, String> replacement, boolean isSecurityEnabled, boolean isJNIEnabled,
String userDefinePolicy) {
getInstance().appType = appType;
getInstance().appCodeBase = appCodeBase;
getInstance().isSecurityEnabled = isSecurityEnabled;
if (!getInstance().isSecurityEnabled) {
return;
}
getInstance().isJNIEnabled = isJNIEnabled;
if (userDefinePolicy == null || userDefinePolicy.length() < "permission".length()) {
userDefinePolicy = "";
}
//delete useless quotes ( from console set)
if (userDefinePolicy.startsWith("\"") && userDefinePolicy.endsWith("\"")) {
userDefinePolicy = userDefinePolicy.substring(1, userDefinePolicy.length() - 1);
} else if (userDefinePolicy.startsWith("'") && userDefinePolicy.endsWith("'")) {
userDefinePolicy = userDefinePolicy.substring(1, userDefinePolicy.length() - 1);
}
userDefinePolicy=userDefinePolicy.trim();
if (!userDefinePolicy.isEmpty() && !userDefinePolicy.endsWith(";")) {
userDefinePolicy = userDefinePolicy + ";";
}
if (getInstance().isJNIEnabled) {
userDefinePolicy += "permission java.lang.RuntimePermission \"loadLibrary.*\";";
try {
if (WareHouse.getInstance().getOdps() != null
&& WareHouse.getInstance().getOdps().getDefaultProject() != null) {
addJavaLibPath(WareHouse.getInstance()
.getResourceDir(WareHouse.getInstance().getOdps().getDefaultProject())
.getAbsolutePath());
}
} catch (IOException e) {
System.err.println("Add java.library.path failed! " + e.getMessage());
}
}
// access warehouse and temp directory is enabled
if (WareHouse.getInstance() != null && WareHouse.getInstance().getWarehouseDir() != null) {
userDefinePolicy +=
"permission java.io.FilePermission \""
+ WareHouse.getInstance().getWarehouseDir().getAbsolutePath() + File.separator + "-"
+ "\" , \"read,write,delete\";";
}
if (WareHouse.getInstance() != null && WareHouse.getInstance().getJobDirStr() != null) {
userDefinePolicy +=
"permission java.io.FilePermission \"" + WareHouse.getInstance().getJobDirStr()
+ File.separator + "-" + "\" , \"read,write,delete\";";
}
getInstance().userDefinePolicy = userDefinePolicy;
getInstance().replacement = replacement;
copyPolicyFile();
}
private static void copyPolicyFile() {
String policyFileName;
switch (getInstance().appType) {
case MR:
policyFileName = "mr.policy";
break;
case UDF:
policyFileName = "udf.policy";
break;
default:
policyFileName = "mr.policy";
break;
}
StringBuffer sb = new StringBuffer();
BufferedReader br = null;
try {
br =
new BufferedReader(new InputStreamReader(SecurityClient.class.getClassLoader()
.getResourceAsStream(policyFileName)));
String line;
while ((line = br.readLine()) != null) {
if (sb.length() > 0) {
sb.append(lineSeperator);
}
sb.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String policyContent = handlePolicyTemplet(sb.toString());
String tempPolicyFilePath = null;
try {
tempPolicyFilePath =
WareHouse.getInstance().getJobDir().getJobDir() + File.separator + "temp."
+ policyFileName;
} catch (Exception exception) {
}
// some application has no warehouse,ie. udf
if (tempPolicyFilePath == null) {
tempPolicyFilePath = "temp." + policyFileName;
}
PrintWriter pw = null;
try {
pw = new PrintWriter(tempPolicyFilePath);
pw.write(policyContent);
pw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
getInstance().policyFilePath = tempPolicyFilePath;
}
private static String handlePolicyTemplet(String policyTemplet) {
// replace module in policy file, like
// $TEMP_HOME$,$WAREHOUSE_HOME$
if (getInstance().replacement != null) {
for (String key : getInstance().replacement.keySet()) {
policyTemplet = policyTemplet.replaceAll(key, getInstance().replacement.get(key));
}
}
policyTemplet = policyTemplet.replace("$USER_DEFINE$", getInstance().userDefinePolicy);
// replace $FRAMEWORK_GRANT$
visitedPath.clear();
StringBuffer frameWorkLibGrant = new StringBuffer();
// odps-sdk-common
String frameWorkLibPath =
ReflectionUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
frameWorkLibGrant.append(grantAllPermission(frameWorkLibPath));
// odps-sdk-core
frameWorkLibPath = Odps.class.getProtectionDomain().getCodeSource().getLocation().getPath();
frameWorkLibGrant.append(lineSeperator);
frameWorkLibGrant.append(grantAllPermission(frameWorkLibPath));
// odps-common-local
frameWorkLibPath =
SecurityClient.class.getProtectionDomain().getCodeSource().getLocation().getPath();
frameWorkLibGrant.append(lineSeperator);
frameWorkLibGrant.append(grantAllPermission(frameWorkLibPath));
// module lib path: odps-mapred-local,odps-graph-local,odps-udf-local
if (getInstance().appCodeBase != null) {
for (String path : getInstance().appCodeBase) {
frameWorkLibPath = path;
frameWorkLibGrant.append(lineSeperator);
frameWorkLibGrant.append(grantAllPermission(frameWorkLibPath));
}
}
policyTemplet = policyTemplet.replace("$FRAMEWORK_GRANT$", frameWorkLibGrant);
return policyTemplet;
}
private static Map<String, Object> visitedPath = new HashMap<String, Object>();
private static Object obj = new Object();
private static String grantAllPermission(String path) {
if (visitedPath.containsKey(path)) {
return "";
}
StringBuffer sb = new StringBuffer();
if (path.endsWith(".jar")) {
// add jar file, for maven project
sb.append("grant codeBase \"file:");
sb.append(path + "\"{");
sb.append(lineSeperator);
sb.append("permission java.security.AllPermission;");
sb.append(lineSeperator);
sb.append("};");
sb.append(lineSeperator);
visitedPath.put(path, obj);
return sb.toString();
// add parent directory
// path = path.substring(0, path.lastIndexOf(File.separator) + 1);
}
path = path.endsWith(File.separator) ? path : path + File.separator;
if (visitedPath.containsKey(path)) {
return sb.toString();
}
sb.append("grant codeBase \"file:");
sb.append(path + "-\"{");
sb.append(lineSeperator);
sb.append("permission java.security.AllPermission;");
sb.append(lineSeperator);
sb.append("};");
sb.append(lineSeperator);
visitedPath.put(path, obj);
return sb.toString();
}
public static boolean isSecurityEnabled() {
return getInstance().isSecurityEnabled;
}
public static boolean isJNIEnabled() {
return getInstance().isJNIEnabled;
}
public static void open() {
if (getInstance().isSecurityEnabled) {
getInstance().oldPolicy = Policy.getPolicy();
getInstance().oldSecurityManager = System.getSecurityManager();
try {
Class policyFileClass = Class.forName("sun.security.provider.PolicyFile");
Constructor constructor = policyFileClass.getConstructor(URL.class);
Policy policy =
(Policy) constructor.newInstance(new URL("file:" + getInstance().policyFilePath));
Policy.setPolicy(policy);
Policy.getPolicy().refresh();
System.setSecurityManager(new DefaultSecurityManager());
} catch (Exception e) {
System.err.println("Your JVM not support local sandbox figture! " + e.getMessage());
}
}
}
public static void close() {
if (getInstance().isSecurityEnabled) {
System.setSecurityManager(getInstance().oldSecurityManager);
Policy.setPolicy(getInstance().oldPolicy);
Policy.getPolicy().refresh();
}
}
private static void addJavaLibPath(String path) throws IOException {
try {
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] oldPaths = (String[]) field.get(null);
for (int i = 0; i < oldPaths.length; i++) {
if (path.trim().equals(oldPaths[i])) {
return;
}
}
String[] newPaths = new String[oldPaths.length + 1];
System.arraycopy(oldPaths, 0, newPaths, 0, oldPaths.length);
newPaths[oldPaths.length] = path;
field.set(null, newPaths);
} catch (IllegalAccessException e) {
throw new IOException(e.getMessage());
} catch (NoSuchFieldException e) {
throw new IOException(e.getMessage());
}
}
/**
* restore environment
*/
public static void clear() {
System.clearProperty(Constants.LOCAL_SECURITY_JNI_ENABLE);
System.clearProperty(Constants.LOCAL_USER_DEFINE_POLICY);
System.clearProperty(Constants.LOCAL_SECURITY_ENABLE);
}
public static void setIsSecurityEnabled(boolean flag) {
System.setProperty(Constants.LOCAL_SECURITY_ENABLE, flag ? "true" : "false");
}
public static void setIsJNIEnabled(boolean flag) {
System.setProperty(Constants.LOCAL_SECURITY_ENABLE, flag ? "true" : "false");
}
public static void setUserDefinePolicy(String userDefinePolicy) {
if (userDefinePolicy != null) {
System.setProperty(Constants.LOCAL_USER_DEFINE_POLICY, userDefinePolicy);
} else {
System.clearProperty(Constants.LOCAL_USER_DEFINE_POLICY);
}
}
}