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

Commit 68dca620 authored by Pranav Madapurmath's avatar Pranav Madapurmath Committed by Android Build Coastguard Worker
Browse files

Resolve account image icon profile boundary exploit.

Because Telecom grants the INTERACT_ACROSS_USERS permission, an exploit
is possible where the user can upload an image icon (belonging to
another user) via registering a phone account. This CL provides a
lightweight solution for parsing the image URI to detect profile
exploitation.

Fixes: 273502295
Fixes: 296915211
Test: Unit test to enforce successful/failure path
(cherry picked from commit d0d1d38e)
(cherry picked from commit e7d0ca3f)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a604311f86ea8136ca2ac9f9ff0af7fa57ee3f42)
Merged-In: I2b6418f019a373ee9f02ba8683e5b694e7ab80a5
Change-Id: I2b6418f019a373ee9f02ba8683e5b694e7ab80a5
parent f177186e
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -601,6 +602,9 @@ public class TelecomServiceImpl {
                                    .build();
                        }

                        // Validate the profile boundary of the given image URI.
                        validateAccountIconUserBoundary(account.getIcon());

                        final long token = Binder.clearCallingIdentity();
                        try {
                            mPhoneAccountRegistrar.registerPhoneAccount(account);
@@ -2754,4 +2758,22 @@ public class TelecomServiceImpl {
            mContext.sendBroadcast(intent);
        }
    }

    private void validateAccountIconUserBoundary(Icon icon) {
        // Refer to Icon#getUriString for context. The URI string is invalid for icons of
        // incompatible types.
        if (icon != null && (icon.getType() == Icon.TYPE_URI
                || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
            String encodedUser = icon.getUri().getEncodedUserInfo();
            // If there is no encoded user, the URI is calling into the calling user space
            if (encodedUser != null) {
                int userId = Integer.parseInt(encodedUser);
                if (userId != UserHandle.getUserId(Binder.getCallingUid())) {
                    // If we are transcending the profile boundary, throw an error.
                    throw new IllegalArgumentException("Attempting to register a phone account with"
                            + " an image icon belonging to another user.");
                }
            }
        }
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -617,6 +618,26 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        }
    }

    @SmallTest
    @Test
    public void testRegisterPhoneAccountImageIconCrossUser() throws RemoteException {
        String packageNameToUse = "com.android.officialpackage";
        PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
                packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
        PhoneAccount phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
        doReturn(PackageManager.PERMISSION_GRANTED)
                .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);

        // This should fail; security exception will be thrown.
        registerPhoneAccountTestHelper(phoneAccount, false);

        icon = Icon.createWithContentUri("content://0@media/external/images/media/");
        phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
        // This should succeed.
        registerPhoneAccountTestHelper(phoneAccount, true);
    }

    @SmallTest
    @Test
    public void testUnregisterPhoneAccount() throws RemoteException {