Merge "Augment SELinuxMMAC functionality."
This commit is contained in:
@@ -48,12 +48,11 @@ public final class SELinuxMMAC {
|
|||||||
private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
|
private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
|
||||||
|
|
||||||
// Signature seinfo values read from policy.
|
// Signature seinfo values read from policy.
|
||||||
private static final HashMap<Signature, String> sSigSeinfo =
|
private static HashMap<Signature, Policy> sSigSeinfo =
|
||||||
new HashMap<Signature, String>();
|
new HashMap<Signature, Policy>();
|
||||||
|
|
||||||
// Package name seinfo values read from policy.
|
// Default seinfo read from policy.
|
||||||
private static final HashMap<String, String> sPackageSeinfo =
|
private static String sDefaultSeinfo = null;
|
||||||
new HashMap<String, String>();
|
|
||||||
|
|
||||||
// Locations of potential install policy files.
|
// Locations of potential install policy files.
|
||||||
private static final File[] INSTALL_POLICY_FILE = {
|
private static final File[] INSTALL_POLICY_FILE = {
|
||||||
@@ -61,9 +60,45 @@ public final class SELinuxMMAC {
|
|||||||
new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
|
new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
|
||||||
null};
|
null};
|
||||||
|
|
||||||
|
// Signature policy stanzas
|
||||||
|
static class Policy {
|
||||||
|
private String seinfo;
|
||||||
|
private final HashMap<String, String> pkgMap;
|
||||||
|
|
||||||
|
Policy() {
|
||||||
|
seinfo = null;
|
||||||
|
pkgMap = new HashMap<String, String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void putSeinfo(String seinfoValue) {
|
||||||
|
seinfo = seinfoValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putPkg(String pkg, String seinfoValue) {
|
||||||
|
pkgMap.put(pkg, seinfoValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid policy stanza means there exists a global
|
||||||
|
// seinfo value or at least one package policy.
|
||||||
|
boolean isValid() {
|
||||||
|
return (seinfo != null) || (!pkgMap.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
String checkPolicy(String pkgName) {
|
||||||
|
// Check for package name seinfo value first.
|
||||||
|
String seinfoValue = pkgMap.get(pkgName);
|
||||||
|
if (seinfoValue != null) {
|
||||||
|
return seinfoValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the global seinfo value.
|
||||||
|
return seinfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void flushInstallPolicy() {
|
private static void flushInstallPolicy() {
|
||||||
sSigSeinfo.clear();
|
sSigSeinfo.clear();
|
||||||
sPackageSeinfo.clear();
|
sDefaultSeinfo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,6 +122,10 @@ public final class SELinuxMMAC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean readInstallPolicy(File[] policyFiles) {
|
private static boolean readInstallPolicy(File[] policyFiles) {
|
||||||
|
// Temp structures to hold the rules while we parse the xml file.
|
||||||
|
// We add all the rules together once we know there's no structural problems.
|
||||||
|
HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>();
|
||||||
|
String defaultSeinfo = null;
|
||||||
|
|
||||||
FileReader policyFile = null;
|
FileReader policyFile = null;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -107,8 +146,6 @@ public final class SELinuxMMAC {
|
|||||||
|
|
||||||
Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
|
Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
|
||||||
|
|
||||||
flushInstallPolicy();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
parser.setInput(policyFile);
|
parser.setInput(policyFile);
|
||||||
@@ -138,57 +175,84 @@ public final class SELinuxMMAC {
|
|||||||
XmlUtils.skipCurrentTag(parser);
|
XmlUtils.skipCurrentTag(parser);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String seinfo = readSeinfoTag(parser);
|
Policy policy = readPolicyTags(parser);
|
||||||
if (seinfo != null) {
|
if (policy.isValid()) {
|
||||||
if (DEBUG_POLICY_INSTALL)
|
sigSeinfo.put(signature, policy);
|
||||||
Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
|
|
||||||
+ seinfo);
|
|
||||||
|
|
||||||
sSigSeinfo.put(signature, seinfo);
|
|
||||||
}
|
}
|
||||||
} else if ("default".equals(tagName)) {
|
} else if ("default".equals(tagName)) {
|
||||||
String seinfo = readSeinfoTag(parser);
|
// Value is null if default tag is absent or seinfo tag is malformed.
|
||||||
if (seinfo != null) {
|
defaultSeinfo = readSeinfoTag(parser);
|
||||||
if (DEBUG_POLICY_INSTALL)
|
if (DEBUG_POLICY_INSTALL)
|
||||||
Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);
|
Slog.i(TAG, "<default> tag assigned seinfo=" + defaultSeinfo);
|
||||||
|
|
||||||
// The 'null' signature is the default seinfo value
|
|
||||||
sSigSeinfo.put(null, seinfo);
|
|
||||||
}
|
|
||||||
} else if ("package".equals(tagName)) {
|
|
||||||
String pkgName = parser.getAttributeValue(null, "name");
|
|
||||||
if (pkgName == null) {
|
|
||||||
Slog.w(TAG, "<package> without name at "
|
|
||||||
+ parser.getPositionDescription());
|
|
||||||
XmlUtils.skipCurrentTag(parser);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String seinfo = readSeinfoTag(parser);
|
|
||||||
if (seinfo != null) {
|
|
||||||
if (DEBUG_POLICY_INSTALL)
|
|
||||||
Slog.i(TAG, "<package> tag: (" + pkgName +
|
|
||||||
") assigned seinfo=" + seinfo);
|
|
||||||
|
|
||||||
sPackageSeinfo.put(pkgName, seinfo);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
XmlUtils.skipCurrentTag(parser);
|
XmlUtils.skipCurrentTag(parser);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
Slog.w(TAG, "Got execption parsing ", e);
|
// An error outside of a stanza means a structural problem
|
||||||
|
// with the xml file. So ignore it.
|
||||||
|
Slog.w(TAG, "Got exception parsing ", e);
|
||||||
|
return false;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.w(TAG, "Got execption parsing ", e);
|
Slog.w(TAG, "Got exception parsing ", e);
|
||||||
}
|
return false;
|
||||||
|
} finally {
|
||||||
try {
|
try {
|
||||||
policyFile.close();
|
policyFile.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
//omit
|
//omit
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flushInstallPolicy();
|
||||||
|
sSigSeinfo = sigSeinfo;
|
||||||
|
sDefaultSeinfo = defaultSeinfo;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Policy readPolicyTags(XmlPullParser parser) throws
|
||||||
|
IOException, XmlPullParserException {
|
||||||
|
|
||||||
|
int type;
|
||||||
|
int outerDepth = parser.getDepth();
|
||||||
|
Policy policy = new Policy();
|
||||||
|
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||||
|
&& (type != XmlPullParser.END_TAG
|
||||||
|
|| parser.getDepth() > outerDepth)) {
|
||||||
|
if (type == XmlPullParser.END_TAG
|
||||||
|
|| type == XmlPullParser.TEXT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tagName = parser.getName();
|
||||||
|
if ("seinfo".equals(tagName)) {
|
||||||
|
String seinfo = parseSeinfo(parser);
|
||||||
|
if (seinfo != null) {
|
||||||
|
policy.putSeinfo(seinfo);
|
||||||
|
}
|
||||||
|
XmlUtils.skipCurrentTag(parser);
|
||||||
|
} else if ("package".equals(tagName)) {
|
||||||
|
String pkg = parser.getAttributeValue(null, "name");
|
||||||
|
if (!validatePackageName(pkg)) {
|
||||||
|
Slog.w(TAG, "<package> without valid name at "
|
||||||
|
+ parser.getPositionDescription());
|
||||||
|
XmlUtils.skipCurrentTag(parser);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String seinfo = readSeinfoTag(parser);
|
||||||
|
if (seinfo != null) {
|
||||||
|
policy.putPkg(pkg, seinfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XmlUtils.skipCurrentTag(parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
private static String readSeinfoTag(XmlPullParser parser) throws
|
private static String readSeinfoTag(XmlPullParser parser) throws
|
||||||
IOException, XmlPullParserException {
|
IOException, XmlPullParserException {
|
||||||
|
|
||||||
@@ -205,19 +269,57 @@ public final class SELinuxMMAC {
|
|||||||
|
|
||||||
String tagName = parser.getName();
|
String tagName = parser.getName();
|
||||||
if ("seinfo".equals(tagName)) {
|
if ("seinfo".equals(tagName)) {
|
||||||
String seinfoValue = parser.getAttributeValue(null, "value");
|
seinfo = parseSeinfo(parser);
|
||||||
if (validateValue(seinfoValue)) {
|
|
||||||
seinfo = seinfoValue;
|
|
||||||
} else {
|
|
||||||
Slog.w(TAG, "<seinfo> without valid value at "
|
|
||||||
+ parser.getPositionDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
XmlUtils.skipCurrentTag(parser);
|
XmlUtils.skipCurrentTag(parser);
|
||||||
}
|
}
|
||||||
return seinfo;
|
return seinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String parseSeinfo(XmlPullParser parser) {
|
||||||
|
|
||||||
|
String seinfoValue = parser.getAttributeValue(null, "value");
|
||||||
|
if (!validateValue(seinfoValue)) {
|
||||||
|
Slog.w(TAG, "<seinfo> without valid value at "
|
||||||
|
+ parser.getPositionDescription());
|
||||||
|
seinfoValue = null;
|
||||||
|
}
|
||||||
|
return seinfoValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General validation routine for package names.
|
||||||
|
* Returns a boolean indicating if the passed string
|
||||||
|
* is a valid android package name.
|
||||||
|
*/
|
||||||
|
private static boolean validatePackageName(String name) {
|
||||||
|
if (name == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
final int N = name.length();
|
||||||
|
boolean hasSep = false;
|
||||||
|
boolean front = true;
|
||||||
|
for (int i=0; i<N; i++) {
|
||||||
|
final char c = name.charAt(i);
|
||||||
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||||
|
front = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!front) {
|
||||||
|
if ((c >= '0' && c <= '9') || c == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == '.') {
|
||||||
|
hasSep = true;
|
||||||
|
front = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasSep;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General validation routine for tag values.
|
* General validation routine for tag values.
|
||||||
* Returns a boolean indicating if the passed string
|
* Returns a boolean indicating if the passed string
|
||||||
@@ -245,10 +347,11 @@ public final class SELinuxMMAC {
|
|||||||
* The label is attached to the ApplicationInfo instance of the package.
|
* The label is attached to the ApplicationInfo instance of the package.
|
||||||
* @param PackageParser.Package object representing the package
|
* @param PackageParser.Package object representing the package
|
||||||
* to labeled.
|
* to labeled.
|
||||||
* @return String holding the value of the seinfo label that was assigned.
|
* @return boolean which determines whether a non null seinfo label
|
||||||
* Value may be null which indicates no seinfo label was assigned.
|
* was assigned to the package. A null value simply meaning that
|
||||||
|
* no policy matched.
|
||||||
*/
|
*/
|
||||||
public static void assignSeinfoValue(PackageParser.Package pkg) {
|
public static boolean assignSeinfoValue(PackageParser.Package pkg) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non system installed apps should be treated the same. This
|
* Non system installed apps should be treated the same. This
|
||||||
@@ -264,31 +367,28 @@ public final class SELinuxMMAC {
|
|||||||
if (s == null)
|
if (s == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sSigSeinfo.containsKey(s)) {
|
Policy policy = sSigSeinfo.get(s);
|
||||||
String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
|
if (policy != null) {
|
||||||
|
String seinfo = policy.checkPolicy(pkg.packageName);
|
||||||
|
if (seinfo != null) {
|
||||||
|
pkg.applicationInfo.seinfo = seinfo;
|
||||||
if (DEBUG_POLICY_INSTALL)
|
if (DEBUG_POLICY_INSTALL)
|
||||||
Slog.i(TAG, "package (" + pkg.packageName +
|
Slog.i(TAG, "package (" + pkg.packageName +
|
||||||
") labeled with seinfo=" + seinfo);
|
") labeled with seinfo=" + seinfo);
|
||||||
|
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for seinfo labeled by package.
|
|
||||||
if (sPackageSeinfo.containsKey(pkg.packageName)) {
|
|
||||||
String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
|
|
||||||
if (DEBUG_POLICY_INSTALL)
|
|
||||||
Slog.i(TAG, "package (" + pkg.packageName +
|
|
||||||
") labeled with seinfo=" + seinfo);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a default seinfo value then great, otherwise
|
// If we have a default seinfo value then great, otherwise
|
||||||
// we set a null object and that is what we started with.
|
// we set a null object and that is what we started with.
|
||||||
String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
|
pkg.applicationInfo.seinfo = sDefaultSeinfo;
|
||||||
if (DEBUG_POLICY_INSTALL)
|
if (DEBUG_POLICY_INSTALL)
|
||||||
Slog.i(TAG, "package (" + pkg.packageName +
|
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with seinfo="
|
||||||
") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
|
+ (sDefaultSeinfo == null ? "null" : sDefaultSeinfo));
|
||||||
|
|
||||||
|
return (sDefaultSeinfo != null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user