• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import android.content.pm.ApplicationInfo;
20 import android.content.pm.PackageParser;
21 import android.content.pm.Signature;
22 import android.os.Environment;
23 import android.util.Slog;
24 import android.util.Xml;
25 
26 import com.android.internal.util.XmlUtils;
27 
28 import java.io.File;
29 import java.io.FileInputStream;
30 import java.io.FileNotFoundException;
31 import java.io.FileReader;
32 import java.io.IOException;
33 
34 import java.util.HashMap;
35 
36 import org.xmlpull.v1.XmlPullParser;
37 import org.xmlpull.v1.XmlPullParserException;
38 
39 /**
40  * Centralized access to SELinux MMAC (middleware MAC) implementation.
41  * {@hide}
42  */
43 public final class SELinuxMMAC {
44 
45     private static final String TAG = "SELinuxMMAC";
46 
47     private static final boolean DEBUG_POLICY = false;
48     private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
49 
50     // Signature seinfo values read from policy.
51     private static final HashMap<Signature, String> sSigSeinfo =
52         new HashMap<Signature, String>();
53 
54     // Package name seinfo values read from policy.
55     private static final HashMap<String, String> sPackageSeinfo =
56         new HashMap<String, String>();
57 
58     // Locations of potential install policy files.
59     private static final File[] INSTALL_POLICY_FILE = {
60         new File(Environment.getDataDirectory(), "security/mac_permissions.xml"),
61         new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
62         null};
63 
flushInstallPolicy()64     private static void flushInstallPolicy() {
65         sSigSeinfo.clear();
66         sPackageSeinfo.clear();
67     }
68 
69     /**
70      * Parses an MMAC install policy from a predefined list of locations.
71      * @param none
72      * @return boolean indicating whether an install policy was correctly parsed.
73      */
readInstallPolicy()74     public static boolean readInstallPolicy() {
75 
76         return readInstallPolicy(INSTALL_POLICY_FILE);
77     }
78 
79     /**
80      * Parses an MMAC install policy given as an argument.
81      * @param File object representing the path of the policy.
82      * @return boolean indicating whether the install policy was correctly parsed.
83      */
readInstallPolicy(File policyFile)84     public static boolean readInstallPolicy(File policyFile) {
85 
86         return readInstallPolicy(new File[]{policyFile,null});
87     }
88 
readInstallPolicy(File[] policyFiles)89     private static boolean readInstallPolicy(File[] policyFiles) {
90 
91         FileReader policyFile = null;
92         int i = 0;
93         while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
94             try {
95                 policyFile = new FileReader(policyFiles[i]);
96                 break;
97             } catch (FileNotFoundException e) {
98                 Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
99             }
100             i++;
101         }
102 
103         if (policyFile == null) {
104             Slog.d(TAG, "No policy file found. All seinfo values will be null.");
105             return false;
106         }
107 
108         Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
109 
110         flushInstallPolicy();
111 
112         try {
113             XmlPullParser parser = Xml.newPullParser();
114             parser.setInput(policyFile);
115 
116             XmlUtils.beginDocument(parser, "policy");
117             while (true) {
118                 XmlUtils.nextElement(parser);
119                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
120                     break;
121                 }
122 
123                 String tagName = parser.getName();
124                 if ("signer".equals(tagName)) {
125                     String cert = parser.getAttributeValue(null, "signature");
126                     if (cert == null) {
127                         Slog.w(TAG, "<signer> without signature at "
128                                + parser.getPositionDescription());
129                         XmlUtils.skipCurrentTag(parser);
130                         continue;
131                     }
132                     Signature signature;
133                     try {
134                         signature = new Signature(cert);
135                     } catch (IllegalArgumentException e) {
136                         Slog.w(TAG, "<signer> with bad signature at "
137                                + parser.getPositionDescription(), e);
138                         XmlUtils.skipCurrentTag(parser);
139                         continue;
140                     }
141                     String seinfo = readSeinfoTag(parser);
142                     if (seinfo != null) {
143                         if (DEBUG_POLICY_INSTALL)
144                             Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
145                                    + seinfo);
146 
147                         sSigSeinfo.put(signature, seinfo);
148                     }
149                 } else if ("default".equals(tagName)) {
150                     String seinfo = readSeinfoTag(parser);
151                     if (seinfo != null) {
152                         if (DEBUG_POLICY_INSTALL)
153                             Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);
154 
155                         // The 'null' signature is the default seinfo value
156                         sSigSeinfo.put(null, seinfo);
157                     }
158                 } else if ("package".equals(tagName)) {
159                     String pkgName = parser.getAttributeValue(null, "name");
160                     if (pkgName == null) {
161                         Slog.w(TAG, "<package> without name at "
162                                + parser.getPositionDescription());
163                         XmlUtils.skipCurrentTag(parser);
164                         continue;
165                     }
166                     String seinfo = readSeinfoTag(parser);
167                     if (seinfo != null) {
168                         if (DEBUG_POLICY_INSTALL)
169                             Slog.i(TAG, "<package> tag: (" + pkgName +
170                                    ") assigned seinfo=" + seinfo);
171 
172                         sPackageSeinfo.put(pkgName, seinfo);
173                     }
174                 } else {
175                     XmlUtils.skipCurrentTag(parser);
176                     continue;
177                 }
178             }
179         } catch (XmlPullParserException e) {
180             Slog.w(TAG, "Got execption parsing ", e);
181         } catch (IOException e) {
182             Slog.w(TAG, "Got execption parsing ", e);
183         }
184         try {
185             policyFile.close();
186         } catch (IOException e) {
187             //omit
188         }
189         return true;
190     }
191 
readSeinfoTag(XmlPullParser parser)192     private static String readSeinfoTag(XmlPullParser parser) throws
193             IOException, XmlPullParserException {
194 
195         int type;
196         int outerDepth = parser.getDepth();
197         String seinfo = null;
198         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
199                && (type != XmlPullParser.END_TAG
200                    || parser.getDepth() > outerDepth)) {
201             if (type == XmlPullParser.END_TAG
202                 || type == XmlPullParser.TEXT) {
203                 continue;
204             }
205 
206             String tagName = parser.getName();
207             if ("seinfo".equals(tagName)) {
208                 String seinfoValue = parser.getAttributeValue(null, "value");
209                 if (validateValue(seinfoValue)) {
210                     seinfo = seinfoValue;
211                 } else {
212                     Slog.w(TAG, "<seinfo> without valid value at "
213                            + parser.getPositionDescription());
214                 }
215             }
216             XmlUtils.skipCurrentTag(parser);
217         }
218         return seinfo;
219     }
220 
221     /**
222      * General validation routine for tag values.
223      * Returns a boolean indicating if the passed string
224      * contains only letters or underscores.
225      */
validateValue(String name)226     private static boolean validateValue(String name) {
227         if (name == null)
228             return false;
229 
230         final int N = name.length();
231         if (N == 0)
232             return false;
233 
234         for (int i = 0; i < N; i++) {
235             final char c = name.charAt(i);
236             if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c != '_')) {
237                 return false;
238             }
239         }
240         return true;
241     }
242 
243     /**
244      * Labels a package based on an seinfo tag from install policy.
245      * The label is attached to the ApplicationInfo instance of the package.
246      * @param PackageParser.Package object representing the package
247      *         to labeled.
248      * @return String holding the value of the seinfo label that was assigned.
249      *         Value may be null which indicates no seinfo label was assigned.
250      */
assignSeinfoValue(PackageParser.Package pkg)251     public static void assignSeinfoValue(PackageParser.Package pkg) {
252 
253         /*
254          * Non system installed apps should be treated the same. This
255          * means that any post-loaded apk will be assigned the default
256          * tag, if one exists in the policy, else null, without respect
257          * to the signing key.
258          */
259         if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
260             ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
261 
262             // We just want one of the signatures to match.
263             for (Signature s : pkg.mSignatures) {
264                 if (s == null)
265                     continue;
266 
267                 if (sSigSeinfo.containsKey(s)) {
268                     String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
269                     if (DEBUG_POLICY_INSTALL)
270                         Slog.i(TAG, "package (" + pkg.packageName +
271                                ") labeled with seinfo=" + seinfo);
272 
273                     return;
274                 }
275             }
276 
277             // Check for seinfo labeled by package.
278             if (sPackageSeinfo.containsKey(pkg.packageName)) {
279                 String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
280                 if (DEBUG_POLICY_INSTALL)
281                     Slog.i(TAG, "package (" + pkg.packageName +
282                            ") labeled with seinfo=" + seinfo);
283                 return;
284             }
285         }
286 
287         // If we have a default seinfo value then great, otherwise
288         // we set a null object and that is what we started with.
289         String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
290         if (DEBUG_POLICY_INSTALL)
291             Slog.i(TAG, "package (" + pkg.packageName +
292                    ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
293     }
294 }
295