Ensure policy has no absurdly long strings

The following APIs now enforce limits and throw IllegalArgumentException
when limits are violated:
* DPM.setTrustAgentConfiguration() limits agent packgage name,
  component name, and strings within configuration bundle.
* DPM.setPermittedAccessibilityServices() limits package names.
* DPM.setPermittedInputMethods() limits package names.
* DPM.setAccountManagementDisabled() limits account name.
* DPM.setLockTaskPackages() limits package names.
* DPM.setAffiliationIds() limits id.
* DPM.transferOwnership() limits strings inside the bundle.

Package names are limited at 223, because they become directory names
and it is a filesystem restriction, see FrameworkParsingPackageUtils.

All other strings are limited at 65535, because longer ones break binary
XML serializer.

The following APIs silently truncate strings that are long beyond reason:
* DPM.setShortSupportMessage() truncates message at 200.
* DPM.setLongSupportMessage() truncates message at 20000.
* DPM.setOrganizationName() truncates org name at 200.

Bug: 260729089
Test: atest com.android.server.devicepolicy
Merged-In: Idcf54e408722f164d16bf2f24a00cd1f5b626d23
Change-Id: Idcf54e408722f164d16bf2f24a00cd1f5b626d23
This commit is contained in:
Pavel Grafov
2023-04-05 15:15:41 +00:00
parent 6b7c751554
commit 12c201509e
2 changed files with 92 additions and 4 deletions

View File

@@ -11262,7 +11262,8 @@ public class DevicePolicyManager {
/**
* Called by a device admin to set the long support message. This will be displayed to the user
* in the device administators settings screen.
* in the device administrators settings screen. If the message is longer than 20000 characters
* it may be truncated.
* <p>
* If the long support message needs to be localized, it is the responsibility of the
* {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast

View File

@@ -360,6 +360,7 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.LocalDate;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -371,6 +372,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -399,6 +401,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572;
// Binary XML serializer doesn't support longer strings
private static final int MAX_POLICY_STRING_LENGTH = 65535;
// FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE, Android packages are used in dir names.
private static final int MAX_PACKAGE_NAME_LENGTH = 223;
private static final int MAX_LONG_SUPPORT_MESSAGE_LENGTH = 20000;
private static final int MAX_SHORT_SUPPORT_MESSAGE_LENGTH = 200;
private static final int MAX_ORG_NAME_LENGTH = 200;
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
@@ -9866,6 +9877,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "admin is null");
Objects.requireNonNull(agent, "agent is null");
enforceMaxPackageNameLength(agent.getPackageName());
final String agentAsString = agent.flattenToString();
enforceMaxStringLength(agentAsString, "agent name");
if (args != null) {
enforceMaxStringLength(args, "args");
}
final int userHandle = UserHandle.getCallingUserId();
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
@@ -10104,6 +10121,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
if (packageList != null) {
for (String pkg : (List<String>) packageList) {
enforceMaxPackageNameLength(pkg);
}
int userId = caller.getUserId();
final List<AccessibilityServiceInfo> enabledServices;
long id = mInjector.binderClearCallingIdentity();
@@ -10270,6 +10291,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (packageList != null) {
for (String pkg : (List<String>) packageList) {
enforceMaxPackageNameLength(pkg);
}
List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
if (enabledImes != null) {
@@ -11494,6 +11519,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
enforceMaxStringLength(accountType, "account type");
final CallerIdentity caller = getCallerIdentity(who);
synchronized (getLockObject()) {
/*
@@ -11912,6 +11939,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throws SecurityException {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(packages, "packages is null");
for (String pkg : packages) {
enforceMaxPackageNameLength(pkg);
}
final CallerIdentity caller = getCallerIdentity(who);
synchronized (getLockObject()) {
@@ -13869,6 +13900,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
message = truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH);
final CallerIdentity caller = getCallerIdentity(who);
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminForUidLocked(who, caller.getUid());
@@ -13901,6 +13934,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
message = truncateIfLonger(message, MAX_LONG_SUPPORT_MESSAGE_LENGTH);
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
synchronized (getLockObject()) {
@@ -14050,6 +14086,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
text = truncateIfLonger(text, MAX_ORG_NAME_LENGTH);
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
if (!TextUtils.equals(admin.organizationName, text)) {
@@ -14298,9 +14336,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalArgumentException("ids must not be null");
}
for (String id : ids) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("ids must not contain empty string");
}
Preconditions.checkArgument(!TextUtils.isEmpty(id), "ids must not have empty string");
enforceMaxStringLength(id, "affiliation id");
}
final Set<String> affiliationIds = new ArraySet<>(ids);
@@ -15557,6 +15594,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"Provided administrator and target are the same object.");
Preconditions.checkArgument(!admin.getPackageName().equals(target.getPackageName()),
"Provided administrator and target have the same package name.");
if (bundle != null) {
enforceMaxStringLength(bundle, "bundle");
}
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -17641,4 +17681,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
&& mInjector.getUsbManager().getUsbHalVersion() >= UsbManager.USB_HAL_V1_3
);
}
/**
* Truncates char sequence to maximum length, nulls are ignored.
*/
private static CharSequence truncateIfLonger(CharSequence input, int maxLength) {
return input == null || input.length() <= maxLength
? input
: input.subSequence(0, maxLength);
}
/**
* Throw if string argument is too long to be serialized.
*/
private static void enforceMaxStringLength(String str, String argName) {
Preconditions.checkArgument(
str.length() <= MAX_POLICY_STRING_LENGTH, argName + " loo long");
}
private static void enforceMaxPackageNameLength(String pkg) {
Preconditions.checkArgument(
pkg.length() <= MAX_PACKAGE_NAME_LENGTH, "Package name too long");
}
/**
* Throw if persistable bundle contains any string that we can't serialize.
*/
private static void enforceMaxStringLength(PersistableBundle bundle, String argName) {
// Persistable bundles can have other persistable bundles as values, traverse with a queue.
Queue<PersistableBundle> queue = new ArrayDeque<>();
queue.add(bundle);
while (!queue.isEmpty()) {
PersistableBundle current = queue.remove();
for (String key : current.keySet()) {
enforceMaxStringLength(key, "key in " + argName);
Object value = current.get(key);
if (value instanceof String) {
enforceMaxStringLength((String) value, "string value in " + argName);
} else if (value instanceof String[]) {
for (String str : (String[]) value) {
enforceMaxStringLength(str, "string value in " + argName);
}
} else if (value instanceof PersistableBundle) {
queue.add((PersistableBundle) value);
}
}
}
}
}