Changing IntentSender to become a listener.

Listener is more secure as nobody can send a broadcast with invalid
digests.

Bug: 160605420
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest
Change-Id: I5f6ec3f0fa975d274d456d11d57be61d0b245e80
This commit is contained in:
Alex Buynytskyy
2021-02-04 10:41:53 -08:00
parent 80f96120be
commit 6f80c512a4
9 changed files with 105 additions and 71 deletions

View File

@@ -12298,7 +12298,7 @@ package android.content.pm {
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12318,6 @@ package android.content.pm {
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12503,10 @@ package android.content.pm {
ctor public PackageManager.NameNotFoundException(String);
}
@java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
}
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();

View File

@@ -39,11 +39,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
@NonNull IntentSender statusReceiver)
@NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
Objects.requireNonNull(packageName);
Objects.requireNonNull(statusReceiver);
Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
try {
if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@ public class ApplicationPackageManager extends PackageManager {
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ "list of certificates.");
}
IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
new IOnChecksumsReadyListener.Stub() {
@Override
public void onChecksumsReady(List<ApkChecksum> checksums)
throws RemoteException {
onChecksumsReadyListener.onChecksumsReady(checksums);
}
};
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
encodeCertificates(trustedInstallers), statusReceiver, getUserId());
encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
getUserId());
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
throw new RuntimeException(e);

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2021 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.content.pm;
import android.content.pm.ApkChecksum;
/**
* Listener that gets notified when checksums are available.
* {@hide}
*/
oneway interface IOnChecksumsReadyListener {
void onChecksumsReady(in List<ApkChecksum> checksums);
}

View File

@@ -27,6 +27,7 @@ import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@ interface IPackageManager {
void notifyPackagesReplacedReceived(in String[] packages);
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
//------------------------------------------------------------------------
//

View File

@@ -3858,13 +3858,6 @@ public abstract class PackageManager {
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
/**
* Extra field name for the ID of a package pending verification. Passed to
* a package verifier and is used to call back to
* @see #requestChecksums
*/
public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
/**
* Permission flag: The permission is set in its current state
* by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@ public abstract class PackageManager {
*/
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
/** Listener that gets notified when checksums are available. */
@FunctionalInterface
public interface OnChecksumsReadyListener {
/**
* Called when the checksums are available.
*
* @param checksums array of checksums.
*/
void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
}
/**
* Requesting the checksums for APKs within a package.
* The checksums will be returned asynchronously via statusReceiver.
* The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
* - enforced by platform,
@@ -8730,15 +8734,14 @@ public abstract class PackageManager {
* {@link #TRUST_ALL} will return checksums from any installer,
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
* otherwise the list has to be non-empty list of certificates.
* @param statusReceiver called once when the results are available as
* {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
* @param onChecksumsReadyListener called once when the results are available.
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
@NonNull IntentSender statusReceiver)
@NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
}

View File

@@ -1137,7 +1137,8 @@ public abstract class PackageManagerInternal {
*/
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
@Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
@Nullable List trustedInstallers,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler);
/**

View File

@@ -23,7 +23,6 @@ import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKE
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@ public class ApkChecksums {
/**
* Fetch or calculate checksums for the collection of files.
*
* @param filesToChecksum split name, null for base and File to fetch checksums for
* @param optional mask to fetch readily available checksums
* @param required mask to forcefully calculate if not available
* @param installerPackageName package name of the installer of the packages
* @param trustedInstallers array of certificate to trust, two specific cases:
* null - trust anybody,
* [] - trust nobody.
* @param statusReceiver to receive the resulting checksums
* @param filesToChecksum split name, null for base and File to fetch checksums for
* @param optional mask to fetch readily available checksums
* @param required mask to forcefully calculate if not available
* @param installerPackageName package name of the installer of the packages
* @param trustedInstallers array of certificate to trust, two specific cases:
* null - trust anybody,
* [] - trust nobody.
* @param onChecksumsReadyListener to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
@Checksum.Type int optional,
@Checksum.Type int required,
@Nullable String installerPackageName,
@Nullable Certificate[] trustedInstallers,
@NonNull IntentSender statusReceiver,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector) {
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@ public class ApkChecksums {
}
long startTime = SystemClock.uptimeMillis();
processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
startTime);
processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
injector, startTime);
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
List<Map<Integer, ApkChecksum>> result,
@Checksum.Type int required,
@NonNull IntentSender statusReceiver,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
@@ -350,7 +349,7 @@ public class ApkChecksums {
// Not ready, come back later.
injector.getHandler().postDelayed(() -> {
processRequiredChecksums(filesToChecksum, result, required,
statusReceiver, injector, startTime);
onChecksumsReadyListener, injector, startTime);
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
return;
}
@@ -363,13 +362,9 @@ public class ApkChecksums {
}
}
final Intent intent = new Intent();
intent.putExtra(EXTRA_CHECKSUMS,
allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
try {
statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
} catch (IntentSender.SendIntentException e) {
onChecksumsReadyListener.onChecksumsReady(allChecksums);
} catch (RemoteException e) {
Slog.w(TAG, e);
}
}

View File

@@ -177,6 +177,7 @@ import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -5521,19 +5522,19 @@ public class PackageManagerService extends IPackageManager.Stub
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
@NonNull IntentSender statusReceiver, int userId) {
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
statusReceiver, userId, mInjector.getBackgroundExecutor(),
onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
@NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
@NonNull Handler handler) {
@Checksum.Type int optional, @Checksum.Type int required,
@Nullable List trustedInstallers,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(statusReceiver);
Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(executor);
Objects.requireNonNull(handler);
@@ -5569,7 +5570,7 @@ public class PackageManagerService extends IPackageManager.Stub
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
trustedCerts, statusReceiver, injector);
trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -27108,12 +27109,14 @@ public class PackageManagerService extends IPackageManager.Stub
ps.setStatesOnCrashOrAnr();
}
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
@Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
@Nullable List trustedInstallers,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
requestChecksumsInternal(packageName, includeSplits, optional, required,
trustedInstallers, statusReceiver, userId, executor, handler);
trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
}
@Override

View File

@@ -16,22 +16,16 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import android.app.admin.SecurityLog;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -39,7 +33,6 @@ import android.util.Slog;
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -109,26 +102,23 @@ public final class ProcessLoggingHandler extends Handler {
// Capturing local loggingInfo to still log even if hash was invalidated.
try {
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
new IntentSender((IIntentSender) new IIntentSender.Stub() {
new IOnChecksumsReadyListener.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType,
IBinder allowlistToken, IIntentReceiver finishedReceiver,
String requiredPermission, Bundle options) {
processChecksums(loggingInfo, intent);
public void onChecksumsReady(List<ApkChecksum> checksums)
throws RemoteException {
processChecksums(loggingInfo, checksums);
}
}), context.getUserId(), mExecutor, this);
}, context.getUserId(),
mExecutor, this);
} catch (Throwable t) {
Slog.e(TAG, "requestChecksums() failed", t);
enqueueProcessChecksum(loggingInfo, null);
}
}
void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
ApkChecksum[].class);
for (ApkChecksum checksum : checksums) {
void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
for (int i = 0, size = checksums.size(); i < size; ++i) {
ApkChecksum checksum = checksums.get(i);
if (checksum.getType() == CHECKSUM_TYPE) {
processChecksum(loggingInfo, checksum.getValue());
return;