Merge "Add ephemeral whitelist for SettingsProvider"

This commit is contained in:
Chad Brubaker
2017-01-20 22:06:55 +00:00
committed by Android (Google) Code Review
2 changed files with 166 additions and 11 deletions

View File

@@ -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;
@@ -1596,6 +1597,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);
@@ -1804,7 +1823,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
@@ -1864,7 +1899,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;
@@ -4020,6 +4067,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<String> 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
*
@@ -6898,6 +6961,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<String> 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.
*
@@ -9906,6 +9983,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<String> 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);
}
}
/**

View File

@@ -271,6 +271,7 @@ public class SettingsProvider extends ContentProvider {
@Override
public boolean onCreate() {
Settings.setInSystemServer();
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
mPackageManager = AppGlobals.getPackageManager();
@@ -829,7 +830,8 @@ public class SettingsProvider extends ContentProvider {
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
List<String> names = settingsState.getSettingNamesLocked();
List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM);
final int nameCount = names.size();
@@ -852,6 +854,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,
@@ -954,8 +959,7 @@ public class SettingsProvider extends ContentProvider {
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
synchronized (mLock) {
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
SETTINGS_TYPE_SECURE, callingUserId);
List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId);
final int nameCount = names.size();
@@ -997,6 +1001,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);
@@ -1159,8 +1166,7 @@ public class SettingsProvider extends ContentProvider {
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
synchronized (mLock) {
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
SETTINGS_TYPE_SYSTEM, callingUserId);
List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId);
final int nameCount = names.size();
@@ -1191,6 +1197,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);
@@ -1409,9 +1418,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);
@@ -1479,6 +1494,55 @@ public class SettingsProvider extends ContentProvider {
}
}
private Set<String> 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<String> getSettingsNamesLocked(int settingsType, int userId) {
ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
if (ai.isEphemeralApp()) {
return new ArrayList<String>(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(
@@ -1548,7 +1612,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;
}