Home | History | Annotate | Download | only in pm
      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