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
* 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

View File

@@ -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());