From 97bccee6d640a62c78676b5e2a1eb3bbe29072af Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Thu, 5 Jan 2017 15:51:41 -0800 Subject: Add ephemeral whitelist for SettingsProvider Currently the list is small, only whats required to launch a basic ephemeral app. It will expand in followup CLs. Note that the goal of this is not to completely shut down all ways that an ephemeral app could learn the value (or part of) of a setting not in the set. The goal is to limit the raw access to settings to a small set that includes settings that ephemeral apps should have access to directly System APIs that are exposed to ephemeral apps may allow for ephemeral apps to learn the value of settings not in the directly exposed set and that is OK and _not_ a security issue. This contains a hack to support code in system system server that in the process of a binder transaction reads a setting using a ContentReceiver with a system package name. This was previously not an issue but causes an exception to be thrown from getCallingPackage which reading a setting now calls. Bug: 33349998 Test: Boots, functions as normal for regular apps. Test: cts-tradefed run cts -m CtsProviderTestCases -t android.provider.cts.SettingsTest Change-Id: Icc839b0d98c725d23cdd395e8cb76a7b293f8767 --- core/java/android/provider/Settings.java | 95 +++++++++++++++++++++- .../providers/settings/SettingsProvider.java | 82 +++++++++++++++++-- 2 files changed, 166 insertions(+), 11 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 18656521f515..ce2774d333d1 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -50,6 +50,7 @@ import android.net.ConnectivityManager; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; +import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.DropBoxManager; @@ -1581,6 +1582,24 @@ public final class Settings { // with a partial enable/disable state in multi-threaded situations. private static final Object mLocationSettingsLock = new Object(); + // Used in system server calling uid workaround in call() + private static boolean sInSystemServer = false; + private static final Object sInSystemServerLock = new Object(); + + /** @hide */ + public static void setInSystemServer() { + synchronized (sInSystemServerLock) { + sInSystemServer = true; + } + } + + /** @hide */ + public static boolean isInSystemServer() { + synchronized (sInSystemServerLock) { + return sInSystemServer; + } + } + public static class SettingNotFoundException extends AndroidException { public SettingNotFoundException(String msg) { super(msg); @@ -1789,7 +1808,23 @@ public final class Settings { } } } - Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + Bundle b; + // If we're in system server and in a binder transaction we need to clear the + // calling uid. This works around code in system server that did not call + // clearCallingIdentity, previously this wasn't needed because reading settings + // did not do permission checking but thats no longer the case. + // Long term this should be removed and callers should properly call + // clearCallingIdentity or use a ContentResolver from the caller as needed. + if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + } if (b != null) { String value = b.getString(Settings.NameValueTable.VALUE); // Don't update our cache for reads of other users' data @@ -1849,7 +1884,19 @@ public final class Settings { try { Bundle queryArgs = ContentResolver.createSqlQueryBundle( NAME_EQ_PLACEHOLDER, new String[]{name}, null); - c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null); + // Same workaround as above. + if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, + null); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, + null); + } if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); return null; @@ -4005,6 +4052,22 @@ public final class Settings { outMap.putAll(CLONE_FROM_PARENT_ON_VALUE); } + /** + * System settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(TEXT_AUTO_REPLACE); + EPHEMERAL_SETTINGS.add(TEXT_AUTO_CAPS); + EPHEMERAL_SETTINGS.add(TEXT_AUTO_PUNCTUATE); + EPHEMERAL_SETTINGS.add(TEXT_SHOW_PASSWORD); + EPHEMERAL_SETTINGS.add(DATE_FORMAT); + EPHEMERAL_SETTINGS.add(FONT_SCALE); + EPHEMERAL_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED); + EPHEMERAL_SETTINGS.add(TIME_12_24); + } + /** * When to use Wi-Fi calling * @@ -6866,6 +6929,20 @@ public final class Settings { outKeySet.addAll(CLONE_TO_MANAGED_PROFILE); } + /** + * Secure settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(ENABLED_ACCESSIBILITY_SERVICES); + EPHEMERAL_SETTINGS.add(ACCESSIBILITY_SPEAK_PASSWORD); + EPHEMERAL_SETTINGS.add(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + + EPHEMERAL_SETTINGS.add(DEFAULT_INPUT_METHOD); + EPHEMERAL_SETTINGS.add(ENABLED_INPUT_METHODS); + } + /** * Helper method for determining if a location provider is enabled. * @@ -9863,6 +9940,20 @@ public final class Settings { * @hide */ public static final String CELL_ON = "cell_on"; + + /** + * Global settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(WAIT_FOR_DEBUGGER); + EPHEMERAL_SETTINGS.add(DEVICE_PROVISIONED); + EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); + EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RTL); + EPHEMERAL_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES); + } + } /** diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3e62158d591c..a29a46debba0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -262,6 +262,7 @@ public class SettingsProvider extends ContentProvider { @Override public boolean onCreate() { + Settings.setInSystemServer(); synchronized (mLock) { mUserManager = UserManager.get(getContext()); mPackageManager = AppGlobals.getPackageManager(); @@ -813,7 +814,8 @@ public class SettingsProvider extends ContentProvider { SettingsState settingsState = mSettingsRegistry.getSettingsLocked( SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); - List names = settingsState.getSettingNamesLocked(); + List names = getSettingsNamesLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM); final int nameCount = names.size(); @@ -836,6 +838,9 @@ public class SettingsProvider extends ContentProvider { Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")"); } + // Ensure the caller can access the setting. + enforceSettingReadable(name, SETTINGS_TYPE_GLOBAL, UserHandle.getCallingUserId()); + // Get the value. synchronized (mLock) { return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL, @@ -938,8 +943,7 @@ public class SettingsProvider extends ContentProvider { final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); synchronized (mLock) { - List names = mSettingsRegistry.getSettingsNamesLocked( - SETTINGS_TYPE_SECURE, callingUserId); + List names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId); final int nameCount = names.size(); @@ -974,6 +978,9 @@ public class SettingsProvider extends ContentProvider { // Resolve the userId on whose behalf the call is made. final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); + // Ensure the caller can access the setting. + enforceSettingReadable(name, SETTINGS_TYPE_SECURE, callingUserId); + // Determine the owning user as some profile settings are cloned from the parent. final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); @@ -1104,8 +1111,7 @@ public class SettingsProvider extends ContentProvider { final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); synchronized (mLock) { - List names = mSettingsRegistry.getSettingsNamesLocked( - SETTINGS_TYPE_SYSTEM, callingUserId); + List names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId); final int nameCount = names.size(); @@ -1136,6 +1142,9 @@ public class SettingsProvider extends ContentProvider { // Resolve the userId on whose behalf the call is made. final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); + // Ensure the caller can access the setting. + enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, callingUserId); + // Determine the owning user as some profile settings are cloned from the parent. final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); @@ -1354,9 +1363,15 @@ public class SettingsProvider extends ContentProvider { && (parentId = getGroupParentLocked(userId)) != userId) { // The setting has a dependency and the profile has a parent String dependency = sSystemCloneFromParentOnDependency.get(setting); - Setting settingObj = getSecureSetting(dependency, userId); - if (settingObj != null && settingObj.getValue().equals("1")) { - return parentId; + // Lookup the dependency setting as ourselves, some callers may not have access to it. + final long token = Binder.clearCallingIdentity(); + try { + Setting settingObj = getSecureSetting(dependency, userId); + if (settingObj != null && settingObj.getValue().equals("1")) { + return parentId; + } + } finally { + Binder.restoreCallingIdentity(token); } } return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting); @@ -1424,6 +1439,55 @@ public class SettingsProvider extends ContentProvider { } } + private Set getEphemeralAccessibleSettings(int settingsType) { + switch (settingsType) { + case SETTINGS_TYPE_GLOBAL: + return Settings.Global.EPHEMERAL_SETTINGS; + case SETTINGS_TYPE_SECURE: + return Settings.Secure.EPHEMERAL_SETTINGS; + case SETTINGS_TYPE_SYSTEM: + return Settings.System.EPHEMERAL_SETTINGS; + default: + throw new IllegalArgumentException("Invalid settings type: " + settingsType); + } + } + + private List getSettingsNamesLocked(int settingsType, int userId) { + ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId); + if (ai.isEphemeralApp()) { + return new ArrayList(getEphemeralAccessibleSettings(settingsType)); + } else { + return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId); + } + } + + private void enforceSettingReadable(String settingName, int settingsType, int userId) { + if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) { + return; + } + ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId); + if (!ai.isEphemeralApp()) { + return; + } + if (!getEphemeralAccessibleSettings(settingsType).contains(settingName)) { + throw new SecurityException("Setting " + settingName + " is not accessible from" + + " ephemeral package " + getCallingPackage()); + } + } + + private ApplicationInfo getCallingApplicationInfoOrThrow(int userId) { + ApplicationInfo ai = null; + try { + ai = mPackageManager.getApplicationInfo(getCallingPackage(), 0 , userId); + } catch (RemoteException ignored) { + } + if (ai == null) { + throw new IllegalStateException("Failed to lookup info for package " + + getCallingPackage()); + } + return ai; + } + private PackageInfo getCallingPackageInfoOrThrow(int userId) { try { PackageInfo packageInfo = mPackageManager.getPackageInfo( @@ -1493,7 +1557,7 @@ public class SettingsProvider extends ContentProvider { value = value.substring(1); Setting settingValue = getSecureSetting( - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); if (settingValue == null) { return false; } -- cgit v1.2.3-59-g8ed1b