Merge "Automatically assign default seinfo labels using the string "default"."

This commit is contained in:
Nick Kralevich
2015-09-01 13:39:10 +00:00
committed by Gerrit Code Review
2 changed files with 54 additions and 188 deletions

View File

@@ -492,12 +492,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
/** /**
* String retrieved from the seinfo tag found in selinux policy. This value * 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 * can be overridden with a value set through the mac_permissions.xml policy
* as its data directory. * 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} * {@hide}
*/ */
public String seinfo; public String seinfo = "default";
/** /**
* Paths to all shared libraries this application is linked against. This * Paths to all shared libraries this application is linked against. This

View File

@@ -139,9 +139,6 @@ public final class SELinuxMMAC {
case "signer": case "signer":
policies.add(readSignerOrThrow(parser)); policies.add(readSignerOrThrow(parser));
break; break;
case "default":
policies.add(readDefaultOrThrow(parser));
break;
default: default:
skip(parser); skip(parser);
} }
@@ -235,45 +232,6 @@ public final class SELinuxMMAC {
return pb.build(); 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 * 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 * 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 * 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 * policy. All signature based policy stanzas are consulted and, if no match is
* is found, the default policy stanza is then consulted. The security label is * found, the default seinfo label of 'default' (set in ApplicationInfo object) is
* attached to the ApplicationInfo instance of the package in the event that a matching * used. The security label is attached to the ApplicationInfo instance of the package
* policy was found. * in the event that a matching policy was found.
* *
* @param pkg object representing the package to be labeled. * @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) { synchronized (sPolicies) {
for (Policy policy : sPolicies) { for (Policy policy : sPolicies) {
String seinfo = policy.getMatchedSeinfo(pkg); String seinfo = policy.getMatchedSeinfo(pkg);
if (seinfo != null) { if (seinfo != null) {
pkg.applicationInfo.seinfo = seinfo; pkg.applicationInfo.seinfo = seinfo;
if (DEBUG_POLICY_INSTALL) { break;
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
"seinfo=" + seinfo);
}
return true;
} }
} }
} }
if (DEBUG_POLICY_INSTALL) { if (DEBUG_POLICY_INSTALL) {
Slog.i(TAG, "package (" + pkg.packageName + ") doesn't match any policy; " + Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
"seinfo will remain null"); "seinfo=" + pkg.applicationInfo.seinfo);
} }
return false;
} }
/** /**
@@ -509,30 +460,16 @@ public final class SELinuxMMAC {
* .build(); * .build();
* } * }
* </pre> * </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 { final class Policy {
private final String mSeinfo; private final String mSeinfo;
private final boolean mDefaultStanza;
private final Set<Signature> mCerts; private final Set<Signature> mCerts;
private final Map<String, String> mPkgMap; private final Map<String, String> mPkgMap;
// Use the PolicyBuilder pattern to instantiate // Use the PolicyBuilder pattern to instantiate
private Policy(PolicyBuilder builder) { private Policy(PolicyBuilder builder) {
mSeinfo = builder.mSeinfo; mSeinfo = builder.mSeinfo;
mDefaultStanza = builder.mDefaultStanza;
mCerts = Collections.unmodifiableSet(builder.mCerts); mCerts = Collections.unmodifiableSet(builder.mCerts);
mPkgMap = Collections.unmodifiableMap(builder.mPkgMap); mPkgMap = Collections.unmodifiableMap(builder.mPkgMap);
} }
@@ -547,15 +484,6 @@ final class Policy {
return mCerts; 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. * Return whether this policy object contains package name mapping refinements.
* *
@@ -587,10 +515,6 @@ final class Policy {
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (mDefaultStanza) {
sb.append("defaultStanza=true ");
}
for (Signature cert : mCerts) { for (Signature cert : mCerts) {
sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... "); sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... ");
} }
@@ -612,22 +536,15 @@ final class Policy {
* is determined using the following steps: * is determined using the following steps:
* </p> * </p>
* <ul> * <ul>
* <li> If this Policy instance is defined as a default stanza: * <li> All certs used to sign the apk and all certs stored with this policy
* <ul><li>Return the global seinfo value</li></ul> * instance are tested for set equality. If this fails then null is returned.
* </li> * </li>
* <li> If this Policy instance is defined as a signer stanza: * <li> If all certs match then an appropriate inner package stanza is
* <ul> * searched based on package name alone. If matched, the stored seinfo
* <li> All certs used to sign the apk and all certs stored with this policy * value for that mapping is returned.
* instance are tested for set equality. If this fails then null is returned. * </li>
* </li> * <li> If all certs matched and no inner package stanza matches then return
* <li> If all certs match then an appropriate inner package stanza is * the global seinfo value. The returned value can be null in this case.
* 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> * </li>
* </ul> * </ul>
* <p> * <p>
@@ -639,37 +556,34 @@ final class Policy {
* A value of null can also be returned if no match occured. * A value of null can also be returned if no match occured.
*/ */
public String getMatchedSeinfo(PackageParser.Package pkg) { public String getMatchedSeinfo(PackageParser.Package pkg) {
if (!mDefaultStanza) { // Check for exact signature matches across all certs.
// Check for exact signature matches across all certs. Signature[] certs = mCerts.toArray(new Signature[0]);
Signature[] certs = mCerts.toArray(new Signature[0]); if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
if (!Signature.areExactMatch(certs, pkg.mSignatures)) { return null;
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;
}
} }
// 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; return mSeinfo;
} }
/** /**
* A nested builder class to create {@link Policy} instances. A {@link Policy} * 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 * 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 * file. A valid policy stanza is defined to be a signer stanza which obeys the rules
* which obeys the rules outlined in external/sepolicy/mac_permissions.xml. The * outlined in external/sepolicy/mac_permissions.xml. The {@link #build} method
* {@link #build} method ensures a set of invariants are upheld enforcing the correct * ensures a set of invariants are upheld enforcing the correct stanza structure
* stanza structure before returning a valid Policy object. * before returning a valid Policy object.
*/ */
public static final class PolicyBuilder { public static final class PolicyBuilder {
private String mSeinfo; private String mSeinfo;
private boolean mDefaultStanza;
private final Set<Signature> mCerts; private final Set<Signature> mCerts;
private final Map<String, String> mPkgMap; private final Map<String, String> mPkgMap;
@@ -678,19 +592,6 @@ final class Policy {
mPkgMap = new HashMap<String, String>(2); 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 * 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 * 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 * 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 * when attached to a signer tag represents the assignment when there isn't a
* its context. When attached to a signer tag the global seinfo represents an * further inner package refinement in policy.
* 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.
* *
* @param seinfo the seinfo value given as a String. * @param seinfo the seinfo value given as a String.
* @return The reference to this PolicyBuilder. * @return The reference to this PolicyBuilder.
@@ -743,9 +641,7 @@ final class Policy {
/** /**
* Create a package name to seinfo value mapping. Each mapping represents * Create a package name to seinfo value mapping. Each mapping represents
* the seinfo value that will be assigned to the described package name. * the seinfo value that will be assigned to the described package name.
* These localized mappings allow the global seinfo to be overriden. This * These localized mappings allow the global seinfo to be overriden.
* mapping provides no value when used in conjunction with a default stanza;
* enforced through the {@link #build} method.
* *
* @param pkgName the android package name given to the app * @param pkgName the android package name given to the app
* @param seinfo the seinfo value that will be assigned to the passed pkgName * @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. * about the expected structure of a policy stanza.
* Those invariants are: * Those invariants are:
* </p> * </p>
* <ul> * <ul>
* <li> If a default stanza * <li> at least one cert must be found </li>
* <ul> * <li> either a global seinfo value is present OR at least one
* <li> an attached global seinfo tag must be present </li> * inner package mapping must be present BUT not both. </li>
* <li> no signatures and no package names can be present </li> * </ul>
* </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>
*
* @return an instance of {@link Policy} with the options set from this builder * @return an instance of {@link Policy} with the options set from this builder
* @throws IllegalStateException if an invariant is violated. * @throws IllegalStateException if an invariant is violated.
*/ */
public Policy build() { public Policy build() {
Policy p = new Policy(this); Policy p = new Policy(this);
if (p.mDefaultStanza) { if (p.mCerts.isEmpty()) {
if (p.mSeinfo == null) { String err = "Missing certs with signer tag. Expecting at least one.";
String err = "Missing global seinfo tag with default stanza."; throw new IllegalStateException(err);
throw new IllegalStateException(err); }
} if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
if (p.mCerts.size() != 0) { String err = "Only seinfo tag XOR package tags are allowed within " +
String err = "Certs not allowed with default stanza."; "a signer stanza.";
throw new IllegalStateException(err); 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);
}
} }
return p; return p;
@@ -861,7 +731,6 @@ final class Policy {
* <ul> * <ul>
* <li> signer stanzas with inner package mappings </li> * <li> signer stanzas with inner package mappings </li>
* <li> signer stanzas with global seinfo tags </li> * <li> signer stanzas with global seinfo tags </li>
* <li> default stanza </li>
* </ul> * </ul>
* This comparison also checks for duplicate entries on the input selectors. Any * This comparison also checks for duplicate entries on the input selectors. Any
* found duplicates will be flagged and can be checked with {@link #foundDuplicate}. * found duplicates will be flagged and can be checked with {@link #foundDuplicate}.
@@ -878,11 +747,6 @@ final class PolicyComparator implements Comparator<Policy> {
@Override @Override
public int compare(Policy p1, Policy p2) { 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 // Give precedence to stanzas with inner package mappings
if (p1.hasInnerPackages() != p2.hasInnerPackages()) { if (p1.hasInnerPackages() != p2.hasInnerPackages()) {
return p1.hasInnerPackages() ? -1 : 1; return p1.hasInnerPackages() ? -1 : 1;
@@ -890,7 +754,7 @@ final class PolicyComparator implements Comparator<Policy> {
// Check for duplicate entries // Check for duplicate entries
if (p1.getSignatures().equals(p2.getSignatures())) { 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()) { if (p1.hasGlobalSeinfo()) {
duplicateFound = true; duplicateFound = true;
Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString()); Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString());