1 /* 2 * Copyright 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.managedprovisioning.task; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.content.Context; 22 import android.content.pm.PackageInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.Signature; 25 import android.text.TextUtils; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.managedprovisioning.analytics.MetricsWriterFactory; 29 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker; 30 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences; 31 import com.android.managedprovisioning.common.ProvisionLogger; 32 import com.android.managedprovisioning.common.SettingsFacade; 33 import com.android.managedprovisioning.common.StoreUtils; 34 import com.android.managedprovisioning.common.Utils; 35 import com.android.managedprovisioning.model.PackageDownloadInfo; 36 import com.android.managedprovisioning.model.ProvisioningParams; 37 38 import java.io.File; 39 import java.util.Arrays; 40 import java.util.LinkedList; 41 import java.util.List; 42 43 /** 44 * Verifies the management app apk downloaded previously in {@link DownloadPackageTask}. 45 * 46 * <p>The first check verifies that a {@link android.app.admin.DeviceAdminReceiver} is present in 47 * the apk and that it corresponds to the one provided via 48 * {@link ProvisioningParams#deviceAdminComponentName}.</p> 49 * 50 * <p>The second check verifies that the package or signature checksum matches the ones given via 51 * {@link PackageDownloadInfo#packageChecksum} or {@link PackageDownloadInfo#signatureChecksum} 52 * respectively. The package checksum takes priority in case both are present.</p> 53 */ 54 public class VerifyPackageTask extends AbstractProvisioningTask { 55 public static final int ERROR_HASH_MISMATCH = 0; 56 public static final int ERROR_DEVICE_ADMIN_MISSING = 1; 57 58 private final Utils mUtils; 59 private final PackageLocationProvider mDownloadLocationProvider; 60 private final PackageManager mPackageManager; 61 private final PackageDownloadInfo mDownloadInfo; 62 VerifyPackageTask( PackageLocationProvider downloadLocationProvider, Context context, ProvisioningParams params, Callback callback)63 public VerifyPackageTask( 64 PackageLocationProvider downloadLocationProvider, 65 Context context, 66 ProvisioningParams params, 67 Callback callback) { 68 this(new Utils(), downloadLocationProvider, context, params, callback, 69 new ProvisioningAnalyticsTracker( 70 MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()), 71 new ManagedProvisioningSharedPreferences(context))); 72 } 73 74 @VisibleForTesting VerifyPackageTask( Utils utils, PackageLocationProvider downloadLocationProvider, Context context, ProvisioningParams params, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)75 VerifyPackageTask( 76 Utils utils, 77 PackageLocationProvider downloadLocationProvider, 78 Context context, 79 ProvisioningParams params, 80 Callback callback, 81 ProvisioningAnalyticsTracker provisioningAnalyticsTracker) { 82 super(context, params, callback, provisioningAnalyticsTracker); 83 84 mUtils = checkNotNull(utils); 85 mDownloadLocationProvider = checkNotNull(downloadLocationProvider); 86 mPackageManager = mContext.getPackageManager(); 87 mDownloadInfo = checkNotNull(params.deviceAdminDownloadInfo); 88 } 89 90 @Override run(int userId)91 public void run(int userId) { 92 final File packageLocation = mDownloadLocationProvider.getPackageLocation(); 93 if (packageLocation == null) { 94 ProvisionLogger.logw("VerifyPackageTask invoked, but package is null"); 95 success(); 96 return; 97 } 98 ProvisionLogger.logi("Verifying package from location " + packageLocation.getAbsolutePath() 99 + " for user " + userId); 100 101 PackageInfo packageInfo = mPackageManager.getPackageArchiveInfo( 102 packageLocation.getAbsolutePath(), 103 PackageManager.GET_SIGNATURES | PackageManager.GET_RECEIVERS); 104 String packageName = mProvisioningParams.inferDeviceAdminPackageName(); 105 // Device admin package name can't be null 106 if (packageInfo == null || packageName == null) { 107 ProvisionLogger.loge("Device admin package info or name is null"); 108 error(ERROR_DEVICE_ADMIN_MISSING); 109 return; 110 } 111 112 if (mUtils.findDeviceAdminInPackageInfo(packageName, 113 mProvisioningParams.deviceAdminComponentName, packageInfo) == null) { 114 error(ERROR_DEVICE_ADMIN_MISSING); 115 return; 116 } 117 118 if (mDownloadInfo.packageChecksum.length > 0) { 119 if (!doesPackageHashMatch( 120 packageLocation.getAbsolutePath(), mDownloadInfo.packageChecksum)) { 121 error(ERROR_HASH_MISMATCH); 122 return; 123 } 124 } else { 125 if (!doesASignatureHashMatch(packageInfo, mDownloadInfo.signatureChecksum)) { 126 error(ERROR_HASH_MISMATCH); 127 return; 128 } 129 } 130 131 success(); 132 } 133 computeHashesOfAllSignatures(Signature[] signatures)134 private List<byte[]> computeHashesOfAllSignatures(Signature[] signatures) { 135 if (signatures == null) { 136 return null; 137 } 138 139 List<byte[]> hashes = new LinkedList<>(); 140 for (Signature signature : signatures) { 141 byte[] hash = mUtils.computeHashOfByteArray(signature.toByteArray()); 142 hashes.add(hash); 143 } 144 return hashes; 145 } 146 doesASignatureHashMatch(PackageInfo packageInfo, byte[] signatureChecksum)147 private boolean doesASignatureHashMatch(PackageInfo packageInfo, byte[] signatureChecksum) { 148 // Check whether a signature hash of downloaded apk matches the hash given in constructor. 149 ProvisionLogger.logd("Checking " + Utils.SHA256_TYPE 150 + "-hashes of all signatures of downloaded package."); 151 List<byte[]> sigHashes = computeHashesOfAllSignatures(packageInfo.signatures); 152 if (sigHashes == null || sigHashes.isEmpty()) { 153 ProvisionLogger.loge("Downloaded package does not have any signatures."); 154 return false; 155 } 156 157 for (byte[] sigHash : sigHashes) { 158 if (Arrays.equals(sigHash, signatureChecksum)) { 159 return true; 160 } 161 } 162 163 ProvisionLogger.loge("Provided hash does not match any signature hash."); 164 ProvisionLogger.loge("Hash provided by programmer: " 165 + StoreUtils.byteArrayToString(signatureChecksum)); 166 ProvisionLogger.loge("Hashes computed from package signatures: "); 167 for (byte[] sigHash : sigHashes) { 168 if (sigHash != null) { 169 ProvisionLogger.loge(StoreUtils.byteArrayToString(sigHash)); 170 } 171 } 172 173 return false; 174 } 175 176 /** 177 * Check whether package hash of downloaded file matches the hash given in PackageDownloadInfo. 178 * By default, SHA-256 is used to verify the file hash. 179 */ doesPackageHashMatch(String downloadLocation, byte[] packageChecksum)180 private boolean doesPackageHashMatch(String downloadLocation, byte[] packageChecksum) { 181 byte[] packageSha256Hash = null; 182 183 ProvisionLogger.logd("Checking file hash of entire apk file."); 184 packageSha256Hash = mUtils.computeHashOfFile(downloadLocation, Utils.SHA256_TYPE); 185 if (Arrays.equals(packageChecksum, packageSha256Hash)) { 186 return true; 187 } 188 189 ProvisionLogger.loge("Provided hash does not match file hash."); 190 ProvisionLogger.loge("Hash provided by programmer: " 191 + StoreUtils.byteArrayToString(packageChecksum)); 192 if (packageSha256Hash != null) { 193 ProvisionLogger.loge("SHA-256 Hash computed from file: " 194 + StoreUtils.byteArrayToString(packageSha256Hash)); 195 } 196 return false; 197 } 198 } 199