SELinuxMMAC.java revision 85d1b5c414f094dae92a33b8c709be76aefe4f17
10596faeddefbf198de137d5e893708495ab1584cFredrik Roubert/* 264339d36f8bd4db5025fe2988eda22b491a9219cFredrik Roubert * Copyright (C) 2012 The Android Open Source Project 3ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 485bf2e2fbc60a9f938064abc8127d61da7d19882Claire Ho * Licensed under the Apache License, Version 2.0 (the "License"); 5ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * you may not use this file except in compliance with the License. 6ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * You may obtain a copy of the License at 7ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 8ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * http://www.apache.org/licenses/LICENSE-2.0 9ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 10ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software 11ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS, 12ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * See the License for the specific language governing permissions and 14ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * limitations under the License. 15ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */ 16ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 17ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Querupackage com.android.server.pm; 18ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 19ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.content.pm.PackageParser; 20ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.content.pm.Signature; 21ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.content.pm.PackageParser.SigningDetails; 22ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.os.Environment; 23ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.util.Slog; 24ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport android.util.Xml; 25ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 26ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport libcore.io.IoUtils; 27ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 28ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport org.xmlpull.v1.XmlPullParser; 29ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport org.xmlpull.v1.XmlPullParserException; 30ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 31ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.io.File; 32ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.io.FileReader; 33ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.io.IOException; 34ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.ArrayList; 35ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.Collections; 36ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.Comparator; 37ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.HashMap; 38ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.HashSet; 39ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.List; 40ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.Map; 41ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruimport java.util.Set; 42ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 43ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru/** 44ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Centralized access to SELinux MMAC (middleware MAC) implementation. This 45ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * class is responsible for loading the appropriate mac_permissions.xml file 46ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * as well as providing an interface for assigning seinfo values to apks. 47ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 48ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * {@hide} 49ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */ 50ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Querupublic final class SELinuxMMAC { 51ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 52ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru static final String TAG = "SELinuxMMAC"; 53ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 54ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static final boolean DEBUG_POLICY = false; 55ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false; 56ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static final boolean DEBUG_POLICY_ORDER = DEBUG_POLICY || false; 57ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 58ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // All policy stanzas read from mac_permissions.xml. This is also the lock 59ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // to synchronize access during policy load and access attempts. 60ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static List<Policy> sPolicies = new ArrayList<>(); 61ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru /** Whether or not the policy files have been read */ 62ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static boolean sPolicyRead; 63ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 64ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru /** Required MAC permissions files */ 65ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static List<File> sMacPermissions = new ArrayList<>(); 66ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 67ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Append privapp to existing seinfo label 6885bf2e2fbc60a9f938064abc8127d61da7d19882Claire Ho private static final String PRIVILEGED_APP_STR = ":privapp"; 69ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 70ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Append v2 to existing seinfo label 71ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static final String SANDBOX_V2_STR = ":v2"; 72ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 73ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion 74ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static final String TARGETSDKVERSION_STR = ":targetSdkVersion="; 75ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 76ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Only initialize sMacPermissions once. 77ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru static { 78ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Platform mac permissions. 79ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sMacPermissions.add(new File( 80ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml")); 81ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 82ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Vendor mac permissions. 83ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // The filename has been renamed from nonplat_mac_permissions to 84ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // vendor_mac_permissions. Either of them should exist. 85ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru final File vendorMacPermission = new File( 86ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml"); 87ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (vendorMacPermission.exists()) { 88ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sMacPermissions.add(vendorMacPermission); 89ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } else { 90ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // For backward compatibility. 91ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sMacPermissions.add(new File(Environment.getVendorDirectory(), 92ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru "/etc/selinux/nonplat_mac_permissions.xml")); 93ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 94ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 95ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // ODM mac permissions (optional). 96ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru final File odmMacPermission = new File( 97ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml"); 98ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (odmMacPermission.exists()) { 99ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sMacPermissions.add(odmMacPermission); 100ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 101ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 102ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 103ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru /** 104ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Load the mac_permissions.xml file containing all seinfo assignments used to 105ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and 106ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively. 107ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * odm_mac_permissions.xml on /odm partition is optional. For further guidance on 108ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * the proper structure of a mac_permissions.xml file consult the source code 109ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * located at system/sepolicy/private/mac_permissions.xml. 110ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 111ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @return boolean indicating if policy was correctly loaded. A value of false 112ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * typically indicates a structural problem with the xml or incorrectly 113ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * constructed policy stanzas. A value of true means that all stanzas 114ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * were loaded successfully; no partial loading is possible. 115ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */ 116ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru public static boolean readInstallPolicy() { 117ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru synchronized (sPolicies) { 118ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (sPolicyRead) { 119ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return true; 120ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 121ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 122ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 123ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Temp structure to hold the rules while we parse the xml file 124ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru List<Policy> policies = new ArrayList<>(); 125ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 126ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru FileReader policyFile = null; 127ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru XmlPullParser parser = Xml.newPullParser(); 128ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 129ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru final int count = sMacPermissions.size(); 130ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru for (int i = 0; i < count; ++i) { 131ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru final File macPermission = sMacPermissions.get(i); 132ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru try { 133ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru policyFile = new FileReader(macPermission); 134ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Slog.d(TAG, "Using policy file " + macPermission); 135ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 136ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.setInput(policyFile); 137ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.nextTag(); 138ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.require(XmlPullParser.START_TAG, null, "policy"); 139ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 140ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru while (parser.next() != XmlPullParser.END_TAG) { 141ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (parser.getEventType() != XmlPullParser.START_TAG) { 142ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru continue; 143ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 144ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 145ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru switch (parser.getName()) { 146ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru case "signer": 147ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru policies.add(readSignerOrThrow(parser)); 148ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru break; 149ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru default: 150ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru skip(parser); 151ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 152ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 153ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } catch (IllegalStateException | IllegalArgumentException | 154ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru XmlPullParserException ex) { 155ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru StringBuilder sb = new StringBuilder("Exception @"); 156ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sb.append(parser.getPositionDescription()); 157ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sb.append(" while parsing "); 158ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sb.append(macPermission); 159ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sb.append(":"); 160ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sb.append(ex); 161ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Slog.w(TAG, sb.toString()); 162ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return false; 163ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } catch (IOException ioe) { 164ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Slog.w(TAG, "Exception parsing " + macPermission, ioe); 165ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return false; 166ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } finally { 167ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru IoUtils.closeQuietly(policyFile); 168ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 169ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 170ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 171ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Now sort the policy stanzas 172ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru PolicyComparator policySort = new PolicyComparator(); 173ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Collections.sort(policies, policySort); 174ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (policySort.foundDuplicate()) { 175ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Slog.w(TAG, "ERROR! Duplicate entries found parsing mac_permissions.xml files"); 176ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return false; 177ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 178ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 179ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru synchronized (sPolicies) { 180ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sPolicies.clear(); 181ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sPolicies.addAll(policies); 182ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru sPolicyRead = true; 183ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 184ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (DEBUG_POLICY_ORDER) { 185ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru for (Policy policy : sPolicies) { 186ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Slog.d(TAG, "Policy: " + policy.toString()); 187ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 188ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 189ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 190ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 191ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return true; 192ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 193ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 194ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru /** 195ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Loop over a signer tag looking for seinfo, package and cert tags. A {@link Policy} 196ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * instance will be created and returned in the process. During the pass all other 197ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * tag elements will be skipped. 198ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 199ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @param parser an XmlPullParser object representing a signer element. 200ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @return the constructed {@link Policy} instance 201ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IOException 202ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws XmlPullParserException 203ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IllegalArgumentException if any of the validation checks fail while 204ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * parsing tag values. 205ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IllegalStateException if any of the invariants fail when constructing 206ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * the {@link Policy} instance. 207ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */ 208ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static Policy readSignerOrThrow(XmlPullParser parser) throws IOException, 209ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru XmlPullParserException { 210ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 211ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.require(XmlPullParser.START_TAG, null, "signer"); 212ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru Policy.PolicyBuilder pb = new Policy.PolicyBuilder(); 213ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 214ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // Check for a cert attached to the signer tag. We allow a signature 215ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru // to appear as an attribute as well as those attached to cert tags. 216ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String cert = parser.getAttributeValue(null, "signature"); 217ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (cert != null) { 218ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru pb.addSignature(cert); 219ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 220ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 221ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru while (parser.next() != XmlPullParser.END_TAG) { 222ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (parser.getEventType() != XmlPullParser.START_TAG) { 223ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru continue; 224ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 225ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 226ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String tagName = parser.getName(); 227ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if ("seinfo".equals(tagName)) { 228ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String seinfo = parser.getAttributeValue(null, "value"); 229ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru pb.setGlobalSeinfoOrThrow(seinfo); 230ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru readSeinfo(parser); 231ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } else if ("package".equals(tagName)) { 232ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru readPackageOrThrow(parser, pb); 233ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } else if ("cert".equals(tagName)) { 234ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String sig = parser.getAttributeValue(null, "signature"); 235ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru pb.addSignature(sig); 236ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru readCert(parser); 237ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } else { 238ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru skip(parser); 239ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 240ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 241ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 242ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru return pb.build(); 243ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 244ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 245ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru /** 246ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Loop over a package element looking for seinfo child tags. If found return the 247ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * value attribute of the seinfo tag, otherwise return null. All other tags encountered 248ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * will be skipped. 249ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * 250ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @param parser an XmlPullParser object representing a package element. 251ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @param pb a Policy.PolicyBuilder instance to build 252ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IOException 253ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws XmlPullParserException 254ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IllegalArgumentException if any of the validation checks fail while 255ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * parsing tag values. 256ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * @throws IllegalStateException if there is a duplicate seinfo tag for the current 257ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * package tag. 258ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */ 259ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static void readPackageOrThrow(XmlPullParser parser, Policy.PolicyBuilder pb) throws 260ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru IOException, XmlPullParserException { 261ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.require(XmlPullParser.START_TAG, null, "package"); 262ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String pkgName = parser.getAttributeValue(null, "name"); 263ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 264ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru while (parser.next() != XmlPullParser.END_TAG) { 265ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if (parser.getEventType() != XmlPullParser.START_TAG) { 266ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru continue; 267ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 268ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 269ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String tagName = parser.getName(); 270ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru if ("seinfo".equals(tagName)) { 271ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru String seinfo = parser.getAttributeValue(null, "value"); 272ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru pb.addInnerPackageMapOrThrow(pkgName, seinfo); 273ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru readSeinfo(parser); 274ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } else { 275ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru skip(parser); 276ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 277ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 278ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 279ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 280ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru private static void readCert(XmlPullParser parser) throws IOException, 281ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru XmlPullParserException { 282ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.require(XmlPullParser.START_TAG, null, "cert"); 283ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru parser.nextTag(); 284ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru } 285ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru 286 private static void readSeinfo(XmlPullParser parser) throws IOException, 287 XmlPullParserException { 288 parser.require(XmlPullParser.START_TAG, null, "seinfo"); 289 parser.nextTag(); 290 } 291 292 private static void skip(XmlPullParser p) throws IOException, XmlPullParserException { 293 if (p.getEventType() != XmlPullParser.START_TAG) { 294 throw new IllegalStateException(); 295 } 296 int depth = 1; 297 while (depth != 0) { 298 switch (p.next()) { 299 case XmlPullParser.END_TAG: 300 depth--; 301 break; 302 case XmlPullParser.START_TAG: 303 depth++; 304 break; 305 } 306 } 307 } 308 309 /** 310 * Applies a security label to a package based on an seinfo tag taken from a matched 311 * policy. All signature based policy stanzas are consulted and, if no match is 312 * found, the default seinfo label of 'default' (set in ApplicationInfo object) is 313 * used. The security label is attached to the ApplicationInfo instance of the package 314 * in the event that a matching policy was found. 315 * 316 * @param pkg object representing the package to be labeled. 317 */ 318 public static void assignSeInfoValue(PackageParser.Package pkg, boolean isPrivileged, 319 int targetSdkVersion) { 320 synchronized (sPolicies) { 321 if (!sPolicyRead) { 322 if (DEBUG_POLICY) { 323 Slog.d(TAG, "Policy not read"); 324 } 325 return; 326 } 327 for (Policy policy : sPolicies) { 328 String seInfo = policy.getMatchedSeInfo(pkg); 329 if (seInfo != null) { 330 pkg.applicationInfo.seInfo = seInfo; 331 break; 332 } 333 } 334 } 335 336 if (pkg.applicationInfo.targetSandboxVersion == 2) 337 pkg.applicationInfo.seInfo += SANDBOX_V2_STR; 338 339 if (isPrivileged) { 340 pkg.applicationInfo.seInfo += PRIVILEGED_APP_STR; 341 } 342 343 pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + targetSdkVersion; 344 345 if (DEBUG_POLICY_INSTALL) { 346 Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " + 347 "seinfo=" + pkg.applicationInfo.seInfo); 348 } 349 } 350} 351 352/** 353 * Holds valid policy representations of individual stanzas from a mac_permissions.xml 354 * file. Each instance can further be used to assign seinfo values to apks using the 355 * {@link Policy#getMatchedSeinfo} method. To create an instance of this use the 356 * {@link PolicyBuilder} pattern class, where each instance is validated against a set 357 * of invariants before being built and returned. Each instance can be guaranteed to 358 * hold one valid policy stanza as outlined in the system/sepolicy/mac_permissions.xml 359 * file. 360 * <p> 361 * The following is an example of how to use {@link Policy.PolicyBuilder} to create a 362 * signer based Policy instance with only inner package name refinements. 363 * </p> 364 * <pre> 365 * {@code 366 * Policy policy = new Policy.PolicyBuilder() 367 * .addSignature("308204a8...") 368 * .addSignature("483538c8...") 369 * .addInnerPackageMapOrThrow("com.foo.", "bar") 370 * .addInnerPackageMapOrThrow("com.foo.other", "bar") 371 * .build(); 372 * } 373 * </pre> 374 * <p> 375 * The following is an example of how to use {@link Policy.PolicyBuilder} to create a 376 * signer based Policy instance with only a global seinfo tag. 377 * </p> 378 * <pre> 379 * {@code 380 * Policy policy = new Policy.PolicyBuilder() 381 * .addSignature("308204a8...") 382 * .addSignature("483538c8...") 383 * .setGlobalSeinfoOrThrow("paltform") 384 * .build(); 385 * } 386 * </pre> 387 */ 388final class Policy { 389 390 private final String mSeinfo; 391 private final Set<Signature> mCerts; 392 private final Map<String, String> mPkgMap; 393 394 // Use the PolicyBuilder pattern to instantiate 395 private Policy(PolicyBuilder builder) { 396 mSeinfo = builder.mSeinfo; 397 mCerts = Collections.unmodifiableSet(builder.mCerts); 398 mPkgMap = Collections.unmodifiableMap(builder.mPkgMap); 399 } 400 401 /** 402 * Return all the certs stored with this policy stanza. 403 * 404 * @return A set of Signature objects representing all the certs stored 405 * with the policy. 406 */ 407 public Set<Signature> getSignatures() { 408 return mCerts; 409 } 410 411 /** 412 * Return whether this policy object contains package name mapping refinements. 413 * 414 * @return A boolean indicating if this object has inner package name mappings. 415 */ 416 public boolean hasInnerPackages() { 417 return !mPkgMap.isEmpty(); 418 } 419 420 /** 421 * Return the mapping of all package name refinements. 422 * 423 * @return A Map object whose keys are the package names and whose values are 424 * the seinfo assignments. 425 */ 426 public Map<String, String> getInnerPackages() { 427 return mPkgMap; 428 } 429 430 /** 431 * Return whether the policy object has a global seinfo tag attached. 432 * 433 * @return A boolean indicating if this stanza has a global seinfo tag. 434 */ 435 public boolean hasGlobalSeinfo() { 436 return mSeinfo != null; 437 } 438 439 @Override 440 public String toString() { 441 StringBuilder sb = new StringBuilder(); 442 for (Signature cert : mCerts) { 443 sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... "); 444 } 445 446 if (mSeinfo != null) { 447 sb.append("seinfo=" + mSeinfo); 448 } 449 450 for (String name : mPkgMap.keySet()) { 451 sb.append(" " + name + "=" + mPkgMap.get(name)); 452 } 453 454 return sb.toString(); 455 } 456 457 /** 458 * <p> 459 * Determine the seinfo value to assign to an apk. The appropriate seinfo value 460 * is determined using the following steps: 461 * </p> 462 * <ul> 463 * <li> All certs used to sign the apk and all certs stored with this policy 464 * instance are tested for set equality. If this fails then null is returned. 465 * </li> 466 * <li> If all certs match then an appropriate inner package stanza is 467 * searched based on package name alone. If matched, the stored seinfo 468 * value for that mapping is returned. 469 * </li> 470 * <li> If all certs matched and no inner package stanza matches then return 471 * the global seinfo value. The returned value can be null in this case. 472 * </li> 473 * </ul> 474 * <p> 475 * In all cases, a return value of null should be interpreted as the apk failing 476 * to match this Policy instance; i.e. failing this policy stanza. 477 * </p> 478 * @param pkg the apk to check given as a PackageParser.Package object 479 * @return A string representing the seinfo matched during policy lookup. 480 * A value of null can also be returned if no match occured. 481 */ 482 public String getMatchedSeInfo(PackageParser.Package pkg) { 483 // Check for exact signature matches across all certs. 484 Signature[] certs = mCerts.toArray(new Signature[0]); 485 if (pkg.mSigningDetails != SigningDetails.UNKNOWN 486 && !Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) { 487 488 // certs aren't exact match, but the package may have rotated from the known system cert 489 if (certs.length > 1 || !pkg.mSigningDetails.hasCertificate(certs[0])) { 490 return null; 491 } 492 } 493 494 // Check for inner package name matches given that the 495 // signature checks already passed. 496 String seinfoValue = mPkgMap.get(pkg.packageName); 497 if (seinfoValue != null) { 498 return seinfoValue; 499 } 500 501 // Return the global seinfo value. 502 return mSeinfo; 503 } 504 505 /** 506 * A nested builder class to create {@link Policy} instances. A {@link Policy} 507 * class instance represents one valid policy stanza found in a mac_permissions.xml 508 * file. A valid policy stanza is defined to be a signer stanza which obeys the rules 509 * outlined in system/sepolicy/mac_permissions.xml. The {@link #build} method 510 * ensures a set of invariants are upheld enforcing the correct stanza structure 511 * before returning a valid Policy object. 512 */ 513 public static final class PolicyBuilder { 514 515 private String mSeinfo; 516 private final Set<Signature> mCerts; 517 private final Map<String, String> mPkgMap; 518 519 public PolicyBuilder() { 520 mCerts = new HashSet<Signature>(2); 521 mPkgMap = new HashMap<String, String>(2); 522 } 523 524 /** 525 * Adds a signature to the set of certs used for validation checks. The purpose 526 * being that all contained certs will need to be matched against all certs 527 * contained with an apk. 528 * 529 * @param cert the signature to add given as a String. 530 * @return The reference to this PolicyBuilder. 531 * @throws IllegalArgumentException if the cert value fails validation; 532 * null or is an invalid hex-encoded ASCII string. 533 */ 534 public PolicyBuilder addSignature(String cert) { 535 if (cert == null) { 536 String err = "Invalid signature value " + cert; 537 throw new IllegalArgumentException(err); 538 } 539 540 mCerts.add(new Signature(cert)); 541 return this; 542 } 543 544 /** 545 * Set the global seinfo tag for this policy stanza. The global seinfo tag 546 * when attached to a signer tag represents the assignment when there isn't a 547 * further inner package refinement in policy. 548 * 549 * @param seinfo the seinfo value given as a String. 550 * @return The reference to this PolicyBuilder. 551 * @throws IllegalArgumentException if the seinfo value fails validation; 552 * null, zero length or contains non-valid characters [^a-zA-Z_\._0-9]. 553 * @throws IllegalStateException if an seinfo value has already been found 554 */ 555 public PolicyBuilder setGlobalSeinfoOrThrow(String seinfo) { 556 if (!validateValue(seinfo)) { 557 String err = "Invalid seinfo value " + seinfo; 558 throw new IllegalArgumentException(err); 559 } 560 561 if (mSeinfo != null && !mSeinfo.equals(seinfo)) { 562 String err = "Duplicate seinfo tag found"; 563 throw new IllegalStateException(err); 564 } 565 566 mSeinfo = seinfo; 567 return this; 568 } 569 570 /** 571 * Create a package name to seinfo value mapping. Each mapping represents 572 * the seinfo value that will be assigned to the described package name. 573 * These localized mappings allow the global seinfo to be overriden. 574 * 575 * @param pkgName the android package name given to the app 576 * @param seinfo the seinfo value that will be assigned to the passed pkgName 577 * @return The reference to this PolicyBuilder. 578 * @throws IllegalArgumentException if the seinfo value fails validation; 579 * null, zero length or contains non-valid characters [^a-zA-Z_\.0-9]. 580 * Or, if the package name isn't a valid android package name. 581 * @throws IllegalStateException if trying to reset a package mapping with a 582 * different seinfo value. 583 */ 584 public PolicyBuilder addInnerPackageMapOrThrow(String pkgName, String seinfo) { 585 if (!validateValue(pkgName)) { 586 String err = "Invalid package name " + pkgName; 587 throw new IllegalArgumentException(err); 588 } 589 if (!validateValue(seinfo)) { 590 String err = "Invalid seinfo value " + seinfo; 591 throw new IllegalArgumentException(err); 592 } 593 594 String pkgValue = mPkgMap.get(pkgName); 595 if (pkgValue != null && !pkgValue.equals(seinfo)) { 596 String err = "Conflicting seinfo value found"; 597 throw new IllegalStateException(err); 598 } 599 600 mPkgMap.put(pkgName, seinfo); 601 return this; 602 } 603 604 /** 605 * General validation routine for the attribute strings of an element. Checks 606 * if the string is non-null, positive length and only contains [a-zA-Z_\.0-9]. 607 * 608 * @param name the string to validate. 609 * @return boolean indicating if the string was valid. 610 */ 611 private boolean validateValue(String name) { 612 if (name == null) 613 return false; 614 615 // Want to match on [0-9a-zA-Z_.] 616 if (!name.matches("\\A[\\.\\w]+\\z")) { 617 return false; 618 } 619 620 return true; 621 } 622 623 /** 624 * <p> 625 * Create a {@link Policy} instance based on the current configuration. This 626 * method checks for certain policy invariants used to enforce certain guarantees 627 * about the expected structure of a policy stanza. 628 * Those invariants are: 629 * </p> 630 * <ul> 631 * <li> at least one cert must be found </li> 632 * <li> either a global seinfo value is present OR at least one 633 * inner package mapping must be present BUT not both. </li> 634 * </ul> 635 * @return an instance of {@link Policy} with the options set from this builder 636 * @throws IllegalStateException if an invariant is violated. 637 */ 638 public Policy build() { 639 Policy p = new Policy(this); 640 641 if (p.mCerts.isEmpty()) { 642 String err = "Missing certs with signer tag. Expecting at least one."; 643 throw new IllegalStateException(err); 644 } 645 if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) { 646 String err = "Only seinfo tag XOR package tags are allowed within " + 647 "a signer stanza."; 648 throw new IllegalStateException(err); 649 } 650 651 return p; 652 } 653 } 654} 655 656/** 657 * Comparision imposing an ordering on Policy objects. It is understood that Policy 658 * objects can only take one of three forms and ordered according to the following 659 * set of rules most specific to least. 660 * <ul> 661 * <li> signer stanzas with inner package mappings </li> 662 * <li> signer stanzas with global seinfo tags </li> 663 * </ul> 664 * This comparison also checks for duplicate entries on the input selectors. Any 665 * found duplicates will be flagged and can be checked with {@link #foundDuplicate}. 666 */ 667 668final class PolicyComparator implements Comparator<Policy> { 669 670 private boolean duplicateFound = false; 671 672 public boolean foundDuplicate() { 673 return duplicateFound; 674 } 675 676 @Override 677 public int compare(Policy p1, Policy p2) { 678 679 // Give precedence to stanzas with inner package mappings 680 if (p1.hasInnerPackages() != p2.hasInnerPackages()) { 681 return p1.hasInnerPackages() ? -1 : 1; 682 } 683 684 // Check for duplicate entries 685 if (p1.getSignatures().equals(p2.getSignatures())) { 686 // Checks if signer w/o inner package names 687 if (p1.hasGlobalSeinfo()) { 688 duplicateFound = true; 689 Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString()); 690 } 691 692 // Look for common inner package name mappings 693 final Map<String, String> p1Packages = p1.getInnerPackages(); 694 final Map<String, String> p2Packages = p2.getInnerPackages(); 695 if (!Collections.disjoint(p1Packages.keySet(), p2Packages.keySet())) { 696 duplicateFound = true; 697 Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString()); 698 } 699 } 700 701 return 0; 702 } 703} 704