Fix PendingIntent hijacking for adb notifications.

Use an explicit intent and set PendingIntent.FLAG_IMMUTABLE to prevent
someone from modifying the intent from PendingIntent.send(...).

Bug: 153356209

Test: atest AdbNotificationsTest
Test: In bug, install and launch the PoC apk and give it notification
permissions. Then, with USB/Wifi debugging enabled, disconnect and connect
the device to create the adb notification. the PoC apk should not have
permission to display information from
content://com.android.settings.files/my_cache/NOTICE.html.

Change-Id: Ie49aa3cf9b33168cf1435fc2427e95aac7f4609b
(cherry picked from commit 2c03881459)
Exempt-From-Owner-Approval: approved in master
This commit is contained in:
Joshua Duong
2020-04-14 10:56:49 -07:00
parent d53e8618cc
commit ec1980f86b
5 changed files with 179 additions and 63 deletions

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 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 android.debug;
import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.notification.SystemNotificationChannels;
/**
* Utility class for building adb notifications.
* @hide
*/
public final class AdbNotifications {
/**
* Notification channel for tv types.
*/
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
/**
* Builds a notification to show connected state for adb over a transport type.
* @param context the context
* @param transportType the adb transport type.
* @return a newly created Notification for the transport type.
*/
public static Notification createNotification(@NonNull Context context,
byte transportType) {
Resources resources = context.getResources();
int titleId;
int messageId;
if (transportType == AdbTransportType.USB) {
titleId = com.android.internal.R.string.adb_active_notification_title;
messageId = com.android.internal.R.string.adb_active_notification_message;
} else if (transportType == AdbTransportType.WIFI) {
titleId = com.android.internal.R.string.adbwifi_active_notification_title;
messageId = com.android.internal.R.string.adbwifi_active_notification_message;
} else {
throw new IllegalArgumentException(
"createNotification called with unknown transport type=" + transportType);
}
CharSequence title = resources.getText(titleId);
CharSequence message = resources.getText(messageId);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setPackage(context.getPackageManager().resolveActivity(intent,
PackageManager.MATCH_SYSTEM_ONLY).activityInfo.packageName);
PendingIntent pIntent = PendingIntent.getActivityAsUser(context, 0, intent,
PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
return new Notification.Builder(context, SystemNotificationChannels.DEVELOPER_IMPORTANT)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
.setTicker(title)
.setDefaults(0) // please be quiet
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(title)
.setContentText(message)
.setContentIntent(pIntent)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.extend(new Notification.TvExtender()
.setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
.build();
}
}

View File

@@ -142,6 +142,9 @@
<!-- WindowMetricsTest permissions -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- Allow use of PendingIntent.getIntent() -->
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
<application android:theme="@style/Theme" android:supportsRtl="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2020 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 android.debug;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import android.app.Notification;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public final class AdbNotificationsTest {
private Context mContext;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
}
@Test
public void testCreateNotification_UsbTransportType() throws Exception {
CharSequence title = mContext.getResources().getText(
com.android.internal.R.string.adb_active_notification_title);
CharSequence message = mContext.getResources().getText(
com.android.internal.R.string.adb_active_notification_message);
Notification notification = AdbNotifications.createNotification(mContext,
AdbTransportType.USB);
// Verify that the adb notification for usb connections has the correct text.
assertEquals(title, notification.extras.getCharSequence(Notification.EXTRA_TITLE, ""));
assertEquals(message, notification.extras.getCharSequence(Notification.EXTRA_TEXT, ""));
// Verify the PendingIntent has an explicit intent (b/153356209).
assertFalse(TextUtils.isEmpty(notification.contentIntent.getIntent().getPackage()));
}
@Test
public void testCreateNotification_WifiTransportType() throws Exception {
CharSequence title = mContext.getResources().getText(
com.android.internal.R.string.adbwifi_active_notification_title);
CharSequence message = mContext.getResources().getText(
com.android.internal.R.string.adbwifi_active_notification_message);
Notification notification = AdbNotifications.createNotification(mContext,
AdbTransportType.WIFI);
// Verify that the adb notification for usb connections has the correct text.
assertEquals(title, notification.extras.getCharSequence(Notification.EXTRA_TITLE, ""));
assertEquals(message, notification.extras.getCharSequence(Notification.EXTRA_TEXT, ""));
// Verify the PendingIntent has an explicit intent (b/153356209).
assertFalse(TextUtils.isEmpty(notification.contentIntent.getIntent().getPackage()));
}
}

View File

@@ -23,7 +23,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -36,6 +35,7 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.debug.AdbManager;
import android.debug.AdbNotifications;
import android.debug.AdbProtoEnums;
import android.debug.AdbTransportType;
import android.debug.PairDevice;
@@ -69,7 +69,6 @@ import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
@@ -760,40 +759,13 @@ public class AdbDebuggingManager {
// Show when at least one device is connected.
public void showAdbConnectedNotification(boolean show) {
final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title;
if (show == mAdbNotificationShown) {
return;
}
setupNotifications();
if (!mAdbNotificationShown) {
Resources r = mContext.getResources();
CharSequence title = r.getText(titleRes);
CharSequence message = r.getText(
com.android.internal.R.string.adbwifi_active_notification_message);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
intent, 0, null, UserHandle.CURRENT);
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
.setTicker(title)
.setDefaults(0) // please be quiet
.setColor(mContext.getColor(
com.android.internal.R.color
.system_notification_accent_color))
.setContentTitle(title)
.setContentText(message)
.setContentIntent(pi)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.extend(new Notification.TvExtender()
.setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
.build();
Notification notification = AdbNotifications.createNotification(mContext,
AdbTransportType.WIFI);
mAdbNotificationShown = true;
mNotificationManager.notifyAsUser(null, id, notification,
UserHandle.ALL);

View File

@@ -41,6 +41,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
import android.debug.AdbNotifications;
import android.debug.AdbTransportType;
import android.debug.IAdbTransport;
import android.hardware.usb.ParcelableUsbPort;
@@ -1180,7 +1181,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
protected void updateAdbNotification(boolean force) {
if (mNotificationManager == null) return;
final int id = SystemMessage.NOTE_ADB_ACTIVE;
final int titleRes = com.android.internal.R.string.adb_active_notification_title;
if (isAdbEnabled() && mConnected) {
if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return;
@@ -1191,38 +1191,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
if (!mAdbNotificationShown) {
Resources r = mContext.getResources();
CharSequence title = r.getText(titleRes);
CharSequence message = r.getText(
com.android.internal.R.string.adb_active_notification_message);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
intent, 0, null, UserHandle.CURRENT);
Notification notification =
new Notification.Builder(mContext,
SystemNotificationChannels.DEVELOPER_IMPORTANT)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
.setTicker(title)
.setDefaults(0) // please be quiet
.setColor(mContext.getColor(
com.android.internal.R.color
.system_notification_accent_color))
.setContentTitle(title)
.setContentText(message)
.setContentIntent(pi)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.extend(new Notification.TvExtender()
.setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
.build();
Notification notification = AdbNotifications.createNotification(mContext,
AdbTransportType.USB);
mAdbNotificationShown = true;
mNotificationManager.notifyAsUser(null, id, notification,
UserHandle.ALL);
mNotificationManager.notifyAsUser(null, id, notification, UserHandle.ALL);
}
} else if (mAdbNotificationShown) {
mAdbNotificationShown = false;