Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f40dc92 authored by Robert Craig's avatar Robert Craig Committed by repo sync
Browse files

Add seinfo parsing to PackageManagerService.



This patch set allows the PMS to parse the
mac_permissions.xml file which contains the
seinfo values. Each package that is installed
on the device will be assigned an seinfo value
based on policy. This seinfo value will help label
the app process and data directory.  Modifications
include adjustments to ApplicationInfo.java
to store the seinfo tag per package as well as
adjustments to installd to communicate the seinfo
tag to libselinux.

Change-Id: I61ad1ea12fb6a9a6d0b108ec163bc4bf4c954b58
Signed-off-by: default avatarrpcraig <rpcraig@tycho.ncsc.mil>
parent 21a34de7
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -397,6 +397,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
     */
    public String[] resourceDirs;

    /**
     * String retrieved from the seinfo tag found in selinux policy. This value
     * is useful in setting an SELinux security context on the process as well
     * as its data directory.
     *
     * {@hide}
     */
    public String seinfo;

    /**
     * Paths to all shared libraries this application is linked against.  This
     * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
@@ -477,6 +486,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        if (resourceDirs != null) {
            pw.println(prefix + "resourceDirs=" + resourceDirs);
        }
        if (seinfo != null) {
            pw.println(prefix + "seinfo=" + seinfo);
        }
        pw.println(prefix + "dataDir=" + dataDir);
        if (sharedLibraryFiles != null) {
            pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
@@ -544,6 +556,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        publicSourceDir = orig.publicSourceDir;
        nativeLibraryDir = orig.nativeLibraryDir;
        resourceDirs = orig.resourceDirs;
        seinfo = orig.seinfo;
        sharedLibraryFiles = orig.sharedLibraryFiles;
        dataDir = orig.dataDir;
        uid = orig.uid;
@@ -583,6 +596,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        dest.writeString(publicSourceDir);
        dest.writeString(nativeLibraryDir);
        dest.writeStringArray(resourceDirs);
        dest.writeString(seinfo);
        dest.writeStringArray(sharedLibraryFiles);
        dest.writeString(dataDir);
        dest.writeInt(uid);
@@ -621,6 +635,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        publicSourceDir = source.readString();
        nativeLibraryDir = source.readString();
        resourceDirs = source.readStringArray();
        seinfo = source.readString();
        sharedLibraryFiles = source.readStringArray();
        dataDir = source.readString();
        uid = source.readInt();
+1 −1
Original line number Diff line number Diff line
@@ -2194,7 +2194,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            // the PID of the new process, or else throw a RuntimeException.
            Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, null, null);
                    app.info.targetSdkVersion, app.info.seinfo, null);
            BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
            synchronized (bs) {
+3 −1
Original line number Diff line number Diff line
@@ -188,7 +188,7 @@ public final class Installer {
        }
    }

    public int install(String name, int uid, int gid) {
    public int install(String name, int uid, int gid, String seinfo) {
        StringBuilder builder = new StringBuilder("install");
        builder.append(' ');
        builder.append(name);
@@ -196,6 +196,8 @@ public final class Installer {
        builder.append(uid);
        builder.append(' ');
        builder.append(gid);
        builder.append(' ');
        builder.append(seinfo != null ? seinfo : "!");
        return execute(builder.toString());
    }

+16 −4
Original line number Diff line number Diff line
@@ -362,6 +362,9 @@ public class PackageManagerService extends IPackageManager.Stub {
    final HashMap<String, FeatureInfo> mAvailableFeatures =
            new HashMap<String, FeatureInfo>();

    // If mac_permissions.xml was found for seinfo labeling.
    boolean mFoundPolicyFile;

    // All available activities, for your resolving pleasure.
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();
@@ -1029,8 +1032,11 @@ public class PackageManagerService extends IPackageManager.Stub {

            readPermissions();

            mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

            mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                    mSdkVersion, mOnlyCore);

            long startTime = SystemClock.uptimeMillis();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -3694,9 +3700,9 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
    }

    private int createDataDirsLI(String packageName, int uid) {
    private int createDataDirsLI(String packageName, int uid, String seinfo) {
        int[] users = sUserManager.getUserIds();
        int res = mInstaller.install(packageName, uid, uid);
        int res = mInstaller.install(packageName, uid, uid, seinfo);
        if (res < 0) {
            return res;
        }
@@ -4038,6 +4044,10 @@ public class PackageManagerService extends IPackageManager.Stub {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
            }

            if (mFoundPolicyFile) {
                SELinuxMMAC.assignSeinfoValue(pkg);
            }

            pkg.applicationInfo.uid = pkgSetting.appId;
            pkg.mExtras = pkgSetting;

@@ -4176,7 +4186,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                            recovered = true;

                            // And now re-install the app.
                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
                                                   pkg.applicationInfo.seinfo);
                            if (ret == -1) {
                                // Ack should not happen!
                                msg = prefix + pkg.packageName
@@ -4222,7 +4233,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                        Log.v(TAG, "Want this data dir: " + dataPath);
                }
                //invoke installer to do the actual installation
                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
                                           pkg.applicationInfo.seinfo);
                if (ret < 0) {
                    // Error from installer
                    mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+272 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.util.XmlUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import java.util.HashMap;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

/**
 * Centralized access to SELinux MMAC (middleware MAC) implementation.
 * {@hide}
 */
public final class SELinuxMMAC {

    private static final String TAG = "SELinuxMMAC";

    private static final boolean DEBUG_POLICY = false;
    private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;

    // Signature seinfo values read from policy.
    private static final HashMap<Signature, String> sSigSeinfo =
        new HashMap<Signature, String>();

    // Package name seinfo values read from policy.
    private static final HashMap<String, String> sPackageSeinfo =
        new HashMap<String, String>();

    // Locations of potential install policy files.
    private static final File[] INSTALL_POLICY_FILE = {
        new File(Environment.getDataDirectory(), "system/mac_permissions.xml"),
        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
        null};

    private static void flushInstallPolicy() {
        sSigSeinfo.clear();
        sPackageSeinfo.clear();
    }

    /**
     * Parses an MMAC install policy from a predefined list of locations.
     * @param none
     * @return boolean indicating whether an install policy was correctly parsed.
     */
    public static boolean readInstallPolicy() {

        return readInstallPolicy(INSTALL_POLICY_FILE);
    }

    /**
     * Parses an MMAC install policy given as an argument.
     * @param File object representing the path of the policy.
     * @return boolean indicating whether the install policy was correctly parsed.
     */
    public static boolean readInstallPolicy(File policyFile) {

        return readInstallPolicy(new File[]{policyFile,null});
    }

    private static boolean readInstallPolicy(File[] policyFiles) {

        FileReader policyFile = null;
        int i = 0;
        while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
            try {
                policyFile = new FileReader(policyFiles[i]);
                break;
            } catch (FileNotFoundException e) {
                Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
            }
            i++;
        }

        if (policyFile == null) {
            Slog.d(TAG, "No policy file found. All seinfo values will be null.");
            return false;
        }

        Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());

        flushInstallPolicy();

        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(policyFile);

            XmlUtils.beginDocument(parser, "policy");
            while (true) {
                XmlUtils.nextElement(parser);
                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
                    break;
                }

                String tagName = parser.getName();
                if ("signer".equals(tagName)) {
                    String cert = parser.getAttributeValue(null, "signature");
                    if (cert == null) {
                        Slog.w(TAG, "<signer> without signature at "
                               + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    Signature signature;
                    try {
                        signature = new Signature(cert);
                    } catch (IllegalArgumentException e) {
                        Slog.w(TAG, "<signer> with bad signature at "
                               + parser.getPositionDescription(), e);
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    String seinfo = readSeinfoTag(parser);
                    if (seinfo != null) {
                        if (DEBUG_POLICY_INSTALL)
                            Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
                                   + seinfo);

                        sSigSeinfo.put(signature, seinfo);
                    }
                } else if ("default".equals(tagName)) {
                    String seinfo = readSeinfoTag(parser);
                    if (seinfo != null) {
                        if (DEBUG_POLICY_INSTALL)
                            Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);

                        // The 'null' signature is the default seinfo value
                        sSigSeinfo.put(null, seinfo);
                    }
                } else if ("package".equals(tagName)) {
                    String pkgName = parser.getAttributeValue(null, "name");
                    if (pkgName == null) {
                        Slog.w(TAG, "<package> without name at "
                               + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    String seinfo = readSeinfoTag(parser);
                    if (seinfo != null) {
                        if (DEBUG_POLICY_INSTALL)
                            Slog.i(TAG, "<package> tag: (" + pkgName +
                                   ") assigned seinfo=" + seinfo);

                        sPackageSeinfo.put(pkgName, seinfo);
                    }
                } else {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }
        } catch (XmlPullParserException e) {
            Slog.w(TAG, "Got execption parsing ", e);
        } catch (IOException e) {
            Slog.w(TAG, "Got execption parsing ", e);
        }
        try {
            policyFile.close();
        } catch (IOException e) {
            //omit
        }
        return true;
    }

    private static String readSeinfoTag(XmlPullParser parser) throws
            IOException, XmlPullParserException {

        int type;
        int outerDepth = parser.getDepth();
        String seinfo = null;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
               && (type != XmlPullParser.END_TAG
                   || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG
                || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if ("seinfo".equals(tagName)) {
                String seinfoValue = parser.getAttributeValue(null, "value");
                if (seinfoValue != null) {
                    seinfo = seinfoValue;
                } else {
                    Slog.w(TAG, "<seinfo> without value at "
                           + parser.getPositionDescription());
                }
            }
            XmlUtils.skipCurrentTag(parser);
        }
        return seinfo;
    }

    /**
     * Labels a package based on an seinfo tag from install policy.
     * The label is attached to the ApplicationInfo instance of the package.
     * @param PackageParser.Package object representing the package
     *         to labeled.
     * @return String holding the value of the seinfo label that was assigned.
     *         Value may be null which indicates no seinfo label was assigned.
     */
    public static void assignSeinfoValue(PackageParser.Package pkg) {

        /*
         * Non system installed apps should be treated the same. This
         * means that any post-loaded apk will be assigned the default
         * tag, if one exists in the policy, else null, without respect
         * to the signing key.
         */
        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {

            // We just want one of the signatures to match.
            for (Signature s : pkg.mSignatures) {
                if (s == null)
                    continue;

                if (sSigSeinfo.containsKey(s)) {
                    String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
                    if (DEBUG_POLICY_INSTALL)
                        Slog.i(TAG, "package (" + pkg.packageName +
                               ") labeled with seinfo=" + seinfo);

                    return;
                }
            }

            // Check for seinfo labeled by package.
            if (sPackageSeinfo.containsKey(pkg.packageName)) {
                String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
                if (DEBUG_POLICY_INSTALL)
                    Slog.i(TAG, "package (" + pkg.packageName +
                           ") labeled with seinfo=" + seinfo);
                return;
            }
        }

        // If we have a default seinfo value then great, otherwise
        // we set a null object and that is what we started with.
        String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
        if (DEBUG_POLICY_INSTALL)
            Slog.i(TAG, "package (" + pkg.packageName +
                   ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
    }
}