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 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 */ 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 */ 84 public static boolean readInstallPolicy(File policyFile) { 85 86 return readInstallPolicy(new File[]{policyFile,null}); 87 } 88 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 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 */ 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 */ 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