Merge "Automatically assign default seinfo labels using the string "default"."
This commit is contained in:
@@ -492,12 +492,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* String retrieved from the seinfo tag found in selinux policy. This value
|
||||
* is useful in setting an SELinux security context on the process as well
|
||||
* as its data directory.
|
||||
* can be overridden with a value set through the mac_permissions.xml policy
|
||||
* construct. This value is useful in setting an SELinux security context on
|
||||
* the process as well as its data directory. The String default is being used
|
||||
* here to represent a catchall label when no policy matches.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public String seinfo;
|
||||
public String seinfo = "default";
|
||||
|
||||
/**
|
||||
* Paths to all shared libraries this application is linked against. This
|
||||
|
||||
@@ -139,9 +139,6 @@ public final class SELinuxMMAC {
|
||||
case "signer":
|
||||
policies.add(readSignerOrThrow(parser));
|
||||
break;
|
||||
case "default":
|
||||
policies.add(readDefaultOrThrow(parser));
|
||||
break;
|
||||
default:
|
||||
skip(parser);
|
||||
}
|
||||
@@ -235,45 +232,6 @@ public final class SELinuxMMAC {
|
||||
return pb.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over a default element looking for seinfo child tags. A {@link Policy}
|
||||
* instance will be created and returned in the process. All other tags encountered
|
||||
* will be skipped.
|
||||
*
|
||||
* @param parser an XmlPullParser object representing a default element.
|
||||
* @return the constructed {@link Policy} instance
|
||||
* @throws IOException
|
||||
* @throws XmlPullParserException
|
||||
* @throws IllegalArgumentException if any of the validation checks fail while
|
||||
* parsing tag values.
|
||||
* @throws IllegalStateException if any of the invariants fail when constructing
|
||||
* the {@link Policy} instance.
|
||||
*/
|
||||
private static Policy readDefaultOrThrow(XmlPullParser parser) throws IOException,
|
||||
XmlPullParserException {
|
||||
|
||||
parser.require(XmlPullParser.START_TAG, null, "default");
|
||||
Policy.PolicyBuilder pb = new Policy.PolicyBuilder();
|
||||
pb.setAsDefaultPolicy();
|
||||
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String tagName = parser.getName();
|
||||
if ("seinfo".equals(tagName)) {
|
||||
String seinfo = parser.getAttributeValue(null, "value");
|
||||
pb.setGlobalSeinfoOrThrow(seinfo);
|
||||
readSeinfo(parser);
|
||||
} else {
|
||||
skip(parser);
|
||||
}
|
||||
}
|
||||
|
||||
return pb.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over a package element looking for seinfo child tags. If found return the
|
||||
* value attribute of the seinfo tag, otherwise return null. All other tags encountered
|
||||
@@ -340,35 +298,28 @@ public final class SELinuxMMAC {
|
||||
|
||||
/**
|
||||
* Applies a security label to a package based on an seinfo tag taken from a matched
|
||||
* policy. All signature based policy stanzas are consulted first and, if no match
|
||||
* is found, the default policy stanza is then consulted. The security label is
|
||||
* attached to the ApplicationInfo instance of the package in the event that a matching
|
||||
* policy was found.
|
||||
* policy. All signature based policy stanzas are consulted and, if no match is
|
||||
* found, the default seinfo label of 'default' (set in ApplicationInfo object) is
|
||||
* used. The security label is attached to the ApplicationInfo instance of the package
|
||||
* in the event that a matching policy was found.
|
||||
*
|
||||
* @param pkg object representing the package to be labeled.
|
||||
* @return boolean which determines whether a non null seinfo label was assigned
|
||||
* to the package. A null value simply represents that no policy matched.
|
||||
*/
|
||||
public static boolean assignSeinfoValue(PackageParser.Package pkg) {
|
||||
public static void assignSeinfoValue(PackageParser.Package pkg) {
|
||||
synchronized (sPolicies) {
|
||||
for (Policy policy : sPolicies) {
|
||||
String seinfo = policy.getMatchedSeinfo(pkg);
|
||||
if (seinfo != null) {
|
||||
pkg.applicationInfo.seinfo = seinfo;
|
||||
if (DEBUG_POLICY_INSTALL) {
|
||||
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
|
||||
"seinfo=" + seinfo);
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_POLICY_INSTALL) {
|
||||
Slog.i(TAG, "package (" + pkg.packageName + ") doesn't match any policy; " +
|
||||
"seinfo will remain null");
|
||||
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
|
||||
"seinfo=" + pkg.applicationInfo.seinfo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,30 +460,16 @@ public final class SELinuxMMAC {
|
||||
* .build();
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The following is an example of how to use {@link Policy.PolicyBuilder} to create a
|
||||
* default based Policy instance.
|
||||
* </p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* Policy policy = new Policy.PolicyBuilder()
|
||||
* .setAsDefaultPolicy()
|
||||
* .setGlobalSeinfoOrThrow("default")
|
||||
* .build();
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
final class Policy {
|
||||
|
||||
private final String mSeinfo;
|
||||
private final boolean mDefaultStanza;
|
||||
private final Set<Signature> mCerts;
|
||||
private final Map<String, String> mPkgMap;
|
||||
|
||||
// Use the PolicyBuilder pattern to instantiate
|
||||
private Policy(PolicyBuilder builder) {
|
||||
mSeinfo = builder.mSeinfo;
|
||||
mDefaultStanza = builder.mDefaultStanza;
|
||||
mCerts = Collections.unmodifiableSet(builder.mCerts);
|
||||
mPkgMap = Collections.unmodifiableMap(builder.mPkgMap);
|
||||
}
|
||||
@@ -547,15 +484,6 @@ final class Policy {
|
||||
return mCerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this policy object represents a default stanza.
|
||||
*
|
||||
* @return A boolean indicating if this object represents a default policy stanza.
|
||||
*/
|
||||
public boolean isDefaultStanza() {
|
||||
return mDefaultStanza;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this policy object contains package name mapping refinements.
|
||||
*
|
||||
@@ -587,10 +515,6 @@ final class Policy {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (mDefaultStanza) {
|
||||
sb.append("defaultStanza=true ");
|
||||
}
|
||||
|
||||
for (Signature cert : mCerts) {
|
||||
sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... ");
|
||||
}
|
||||
@@ -612,22 +536,15 @@ final class Policy {
|
||||
* is determined using the following steps:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li> If this Policy instance is defined as a default stanza:
|
||||
* <ul><li>Return the global seinfo value</li></ul>
|
||||
* <li> All certs used to sign the apk and all certs stored with this policy
|
||||
* instance are tested for set equality. If this fails then null is returned.
|
||||
* </li>
|
||||
* <li> If this Policy instance is defined as a signer stanza:
|
||||
* <ul>
|
||||
* <li> All certs used to sign the apk and all certs stored with this policy
|
||||
* instance are tested for set equality. If this fails then null is returned.
|
||||
* </li>
|
||||
* <li> If all certs match then an appropriate inner package stanza is
|
||||
* searched based on package name alone. If matched, the stored seinfo
|
||||
* value for that mapping is returned.
|
||||
* </li>
|
||||
* <li> If all certs matched and no inner package stanza matches then return
|
||||
* the global seinfo value. The returned value can be null in this case.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <li> If all certs match then an appropriate inner package stanza is
|
||||
* searched based on package name alone. If matched, the stored seinfo
|
||||
* value for that mapping is returned.
|
||||
* </li>
|
||||
* <li> If all certs matched and no inner package stanza matches then return
|
||||
* the global seinfo value. The returned value can be null in this case.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
@@ -639,37 +556,34 @@ final class Policy {
|
||||
* A value of null can also be returned if no match occured.
|
||||
*/
|
||||
public String getMatchedSeinfo(PackageParser.Package pkg) {
|
||||
if (!mDefaultStanza) {
|
||||
// Check for exact signature matches across all certs.
|
||||
Signature[] certs = mCerts.toArray(new Signature[0]);
|
||||
if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for inner package name matches given that the
|
||||
// signature checks already passed.
|
||||
String seinfoValue = mPkgMap.get(pkg.packageName);
|
||||
if (seinfoValue != null) {
|
||||
return seinfoValue;
|
||||
}
|
||||
// Check for exact signature matches across all certs.
|
||||
Signature[] certs = mCerts.toArray(new Signature[0]);
|
||||
if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the global seinfo value (even if it's null).
|
||||
// Check for inner package name matches given that the
|
||||
// signature checks already passed.
|
||||
String seinfoValue = mPkgMap.get(pkg.packageName);
|
||||
if (seinfoValue != null) {
|
||||
return seinfoValue;
|
||||
}
|
||||
|
||||
// Return the global seinfo value.
|
||||
return mSeinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* A nested builder class to create {@link Policy} instances. A {@link Policy}
|
||||
* class instance represents one valid policy stanza found in a mac_permissions.xml
|
||||
* file. A valid policy stanza is defined to be either a signer or default stanza
|
||||
* which obeys the rules outlined in external/sepolicy/mac_permissions.xml. The
|
||||
* {@link #build} method ensures a set of invariants are upheld enforcing the correct
|
||||
* stanza structure before returning a valid Policy object.
|
||||
* file. A valid policy stanza is defined to be a signer stanza which obeys the rules
|
||||
* outlined in external/sepolicy/mac_permissions.xml. The {@link #build} method
|
||||
* ensures a set of invariants are upheld enforcing the correct stanza structure
|
||||
* before returning a valid Policy object.
|
||||
*/
|
||||
public static final class PolicyBuilder {
|
||||
|
||||
private String mSeinfo;
|
||||
private boolean mDefaultStanza;
|
||||
private final Set<Signature> mCerts;
|
||||
private final Map<String, String> mPkgMap;
|
||||
|
||||
@@ -678,19 +592,6 @@ final class Policy {
|
||||
mPkgMap = new HashMap<String, String>(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this stanza as a default stanza. All policy stanzas are assumed to
|
||||
* be signer stanzas unless this method is explicitly called. Default stanzas
|
||||
* are treated differently with respect to allowable child tags, ordering and
|
||||
* when and how policy decisions are enforced.
|
||||
*
|
||||
* @return The reference to this PolicyBuilder.
|
||||
*/
|
||||
public PolicyBuilder setAsDefaultPolicy() {
|
||||
mDefaultStanza = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a signature to the set of certs used for validation checks. The purpose
|
||||
* being that all contained certs will need to be matched against all certs
|
||||
@@ -713,11 +614,8 @@ final class Policy {
|
||||
|
||||
/**
|
||||
* Set the global seinfo tag for this policy stanza. The global seinfo tag
|
||||
* represents the seinfo element that is used in one of two ways depending on
|
||||
* its context. When attached to a signer tag the global seinfo represents an
|
||||
* assignment when there isn't a further inner package refinement in policy.
|
||||
* When used with a default tag, it represents the only allowable assignment
|
||||
* value.
|
||||
* when attached to a signer tag represents the assignment when there isn't a
|
||||
* further inner package refinement in policy.
|
||||
*
|
||||
* @param seinfo the seinfo value given as a String.
|
||||
* @return The reference to this PolicyBuilder.
|
||||
@@ -743,9 +641,7 @@ final class Policy {
|
||||
/**
|
||||
* Create a package name to seinfo value mapping. Each mapping represents
|
||||
* the seinfo value that will be assigned to the described package name.
|
||||
* These localized mappings allow the global seinfo to be overriden. This
|
||||
* mapping provides no value when used in conjunction with a default stanza;
|
||||
* enforced through the {@link #build} method.
|
||||
* These localized mappings allow the global seinfo to be overriden.
|
||||
*
|
||||
* @param pkgName the android package name given to the app
|
||||
* @param seinfo the seinfo value that will be assigned to the passed pkgName
|
||||
@@ -802,51 +698,25 @@ final class Policy {
|
||||
* about the expected structure of a policy stanza.
|
||||
* Those invariants are:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li> If a default stanza
|
||||
* <ul>
|
||||
* <li> an attached global seinfo tag must be present </li>
|
||||
* <li> no signatures and no package names can be present </li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li> If a signer stanza
|
||||
* <ul>
|
||||
* <li> at least one cert must be found </li>
|
||||
* <li> either a global seinfo value is present OR at least one
|
||||
* inner package mapping must be present BUT not both. </li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <ul>
|
||||
* <li> at least one cert must be found </li>
|
||||
* <li> either a global seinfo value is present OR at least one
|
||||
* inner package mapping must be present BUT not both. </li>
|
||||
* </ul>
|
||||
* @return an instance of {@link Policy} with the options set from this builder
|
||||
* @throws IllegalStateException if an invariant is violated.
|
||||
*/
|
||||
public Policy build() {
|
||||
Policy p = new Policy(this);
|
||||
|
||||
if (p.mDefaultStanza) {
|
||||
if (p.mSeinfo == null) {
|
||||
String err = "Missing global seinfo tag with default stanza.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
if (p.mCerts.size() != 0) {
|
||||
String err = "Certs not allowed with default stanza.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
if (!p.mPkgMap.isEmpty()) {
|
||||
String err = "Inner package mappings not allowed with default stanza.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
} else {
|
||||
if (p.mCerts.size() == 0) {
|
||||
String err = "Missing certs with signer tag. Expecting at least one.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
|
||||
String err = "Only seinfo tag XOR package tags are allowed within " +
|
||||
"a signer stanza.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
if (p.mCerts.isEmpty()) {
|
||||
String err = "Missing certs with signer tag. Expecting at least one.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
|
||||
String err = "Only seinfo tag XOR package tags are allowed within " +
|
||||
"a signer stanza.";
|
||||
throw new IllegalStateException(err);
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -861,7 +731,6 @@ final class Policy {
|
||||
* <ul>
|
||||
* <li> signer stanzas with inner package mappings </li>
|
||||
* <li> signer stanzas with global seinfo tags </li>
|
||||
* <li> default stanza </li>
|
||||
* </ul>
|
||||
* This comparison also checks for duplicate entries on the input selectors. Any
|
||||
* found duplicates will be flagged and can be checked with {@link #foundDuplicate}.
|
||||
@@ -878,11 +747,6 @@ final class PolicyComparator implements Comparator<Policy> {
|
||||
@Override
|
||||
public int compare(Policy p1, Policy p2) {
|
||||
|
||||
// Give precedence to signature stanzas over default stanzas
|
||||
if (p1.isDefaultStanza() != p2.isDefaultStanza()) {
|
||||
return p1.isDefaultStanza() ? 1 : -1;
|
||||
}
|
||||
|
||||
// Give precedence to stanzas with inner package mappings
|
||||
if (p1.hasInnerPackages() != p2.hasInnerPackages()) {
|
||||
return p1.hasInnerPackages() ? -1 : 1;
|
||||
@@ -890,7 +754,7 @@ final class PolicyComparator implements Comparator<Policy> {
|
||||
|
||||
// Check for duplicate entries
|
||||
if (p1.getSignatures().equals(p2.getSignatures())) {
|
||||
// Checks if default stanza or a signer w/o inner package names
|
||||
// Checks if signer w/o inner package names
|
||||
if (p1.hasGlobalSeinfo()) {
|
||||
duplicateFound = true;
|
||||
Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString());
|
||||
|
||||
Reference in New Issue
Block a user