Merge "Add a compat change to opt-in to latest SELinux domain."
diff --git a/Android.bp b/Android.bp
index c1f6860..3c08056 100644
--- a/Android.bp
+++ b/Android.bp
@@ -444,6 +444,7 @@
"services-platform-compat-config",
"media-provider-platform-compat-config",
"services-devicepolicy-platform-compat-config",
+ "services-core-platform-compat-config",
],
static_libs: [
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 2eb2f33..203bc61 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -162,3 +162,9 @@
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
+
+platform_compat_config {
+ name: "services-core-platform-compat-config",
+ src: ":services.core.unboosted",
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8439a0d..4e0e4ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2364,6 +2364,37 @@
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
t.traceEnd(); // "create package manager"
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ packageName -> {
+ synchronized (m.mInstallLock) {
+ final PackageParser.Package pkg;
+ final SharedUserSetting sharedUser;
+ synchronized (m.mLock) {
+ PackageSetting ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
+ }
+ pkg = ps.pkg;
+ sharedUser = ps.sharedUser;
+ }
+
+ if (pkg == null) {
+ Slog.e(TAG, "Failed to find package " + packageName);
+ return;
+ }
+ final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+ m.mInjector.getCompatibility());
+
+ if (!newSeInfo.equals(pkg.applicationInfo.seInfo)) {
+ Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ + pkg.applicationInfo.seInfo + " to: " + newSeInfo);
+ pkg.applicationInfo.seInfo = newSeInfo;
+ m.prepareAppDataAfterInstallLIF(pkg);
+ }
+ }
+ });
+
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
@@ -10784,24 +10815,8 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- // Apps which share a sharedUserId must be placed in the same selinux domain. If this
- // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
- // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
- // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
- // least restrictive selinux domain.
- // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
- // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
- // ensures that all packages continue to run in the same selinux domain.
- final int targetSdkVersion =
- ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
- sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
- // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
- // They currently can be if the sharedUser apps are signed with the platform key.
- final boolean isPrivileged = (sharedUserSetting != null) ?
- sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
-
- pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
- targetSdkVersion);
+ pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, sharedUserSetting,
+ injector.getCompatibility());
pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b464988..d20f20f 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
@@ -23,6 +25,8 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.server.compat.PlatformCompat;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -72,6 +76,19 @@
// Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+ /**
+ * This change gates apps access to untrusted_app_R-targetSDk SELinux domain. Allows opt-in
+ * to R targetSdkVersion enforced changes without changing target SDK. Turning this change
+ * off for an app targeting R is a no-op.
+ *
+ * <p>Has no effect for apps using shared user id.
+ *
+ * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
+ */
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
+ @ChangeId
+ static final long SELINUX_LATEST_CHANGES = 143539591L;
+
// Only initialize sMacPermissions once.
static {
// Platform mac permissions.
@@ -319,6 +336,48 @@
}
}
+ private static int getTargetSdkVersionForSeInfo(PackageParser.Package pkg,
+ SharedUserSetting sharedUserSetting, PlatformCompat compatibility) {
+ // Apps which share a sharedUserId must be placed in the same selinux domain. If this
+ // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
+ // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
+ // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
+ // least restrictive selinux domain.
+ // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
+ // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
+ // ensures that all packages continue to run in the same selinux domain.
+ if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
+ return sharedUserSetting.seInfoTargetSdkVersion;
+ }
+ if (compatibility.isChangeEnabled(SELINUX_LATEST_CHANGES, pkg.applicationInfo)) {
+ return android.os.Build.VERSION_CODES.R;
+ }
+
+ return pkg.applicationInfo.targetSdkVersion;
+ }
+
+ /**
+ * Selects a security label to a package based on input parameters and the seinfo tag taken
+ * from a matched policy. All signature based policy stanzas are consulted and, if no match
+ * is found, the default seinfo label of 'default' is used. The security label is attached to
+ * the ApplicationInfo instance of the package.
+ *
+ * @param pkg object representing the package to be labeled.
+ * @param sharedUserSetting if the app shares a sharedUserId, then this has the shared setting.
+ * @param compatibility the PlatformCompat service to ask about state of compat changes.
+ * @return String representing the resulting seinfo.
+ */
+ public static String getSeInfo(PackageParser.Package pkg, SharedUserSetting sharedUserSetting,
+ PlatformCompat compatibility) {
+ final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUserSetting,
+ compatibility);
+ // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
+ // They currently can be if the sharedUser apps are signed with the platform key.
+ final boolean isPrivileged = (sharedUserSetting != null)
+ ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+ return getSeInfo(pkg, isPrivileged, targetSdkVersion);
+ }
+
/**
* Selects a security label to a package based on input parameters and the seinfo tag taken
* from a matched policy. All signature based policy stanzas are consulted and, if no match
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
new file mode 100644
index 0000000..4b7dd36
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 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 static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageParser;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.compat.PlatformCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+
+/**
+ * {@link SELinuxMMAC} tests.
+ */
+@RunWith(MockitoJUnitRunner.class)
+@Presubmit
+public class SELinuxMMACTest {
+
+ private static final String PACKAGE_NAME = "my.package";
+ private static final int OPT_IN_VERSION = android.os.Build.VERSION_CODES.R;
+
+ @Mock
+ PlatformCompat mMockCompatibility;
+
+ PackageParser.Package mPkg;
+
+ @Before
+ public void setUp() {
+ mPkg = new PackageParser.Package(PACKAGE_NAME);
+ mPkg.applicationInfo.targetSdkVersion = 28;
+ }
+
+ @Test
+ public void getSeInfoOptInToLatest() {
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoNoOptIn() {
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=28"));
+ }
+
+ @Test
+ public void getSeInfoNoOptInButAlreadyR() {
+ mPkg.applicationInfo.targetSdkVersion = OPT_IN_VERSION;
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 3ea3b3c..74ef034 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -48,6 +48,8 @@
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import com.android.server.compat.PlatformCompat;
+
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -71,12 +73,15 @@
@Mock
UserManagerInternal mMockUserManager;
@Mock
+ PlatformCompat mMockCompatibility;
+ @Mock
PackageManagerService.Injector mMockInjector;
@Before
public void setupInjector() {
when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper);
when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager);
+ when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility);
}
@Before