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