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.R; 33 import com.android.managedprovisioning.common.SettingsFacade; 34 import com.android.managedprovisioning.common.StoreUtils; 35 import com.android.managedprovisioning.common.Utils; 36 import com.android.managedprovisioning.model.PackageDownloadInfo; 37 import com.android.managedprovisioning.model.ProvisioningParams; 38 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 DownloadPackageTask mDownloadPackageTask; 60 private final PackageManager mPackageManager; 61 private final PackageDownloadInfo mDownloadInfo; 62 VerifyPackageTask( DownloadPackageTask downloadPackageTask, Context context, ProvisioningParams params, Callback callback)63 public VerifyPackageTask( 64 DownloadPackageTask downloadPackageTask, 65 Context context, 66 ProvisioningParams params, 67 Callback callback) { 68 this(new Utils(), downloadPackageTask, context, params, callback, 69 new ProvisioningAnalyticsTracker( 70 MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()), 71 new ManagedProvisioningSharedPreferences(context))); 72 } 73 74 @VisibleForTesting VerifyPackageTask( Utils utils, DownloadPackageTask downloadPackageTask, Context context, ProvisioningParams params, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)75 VerifyPackageTask( 76 Utils utils, 77 DownloadPackageTask downloadPackageTask, 78 Context context, 79 ProvisioningParams params, 80 Callback callback, 81 ProvisioningAnalyticsTracker provisioningAnalyticsTracker) { 82 super(context, params, callback, provisioningAnalyticsTracker); 83 84 mUtils = checkNotNull(utils); 85 mDownloadPackageTask = checkNotNull(downloadPackageTask); 86 mPackageManager = mContext.getPackageManager(); 87 mDownloadInfo = checkNotNull(params.deviceAdminDownloadInfo); 88 } 89 90 @Override run(int userId)91 public void run(int userId) { 92 final String downloadLocation = mDownloadPackageTask.getDownloadedPackageLocation(); 93 if (TextUtils.isEmpty(downloadLocation)) { 94 ProvisionLogger.logw("VerifyPackageTask invoked, but download location is null"); 95 success(); 96 return; 97 } 98 99 PackageInfo packageInfo = mPackageManager.getPackageArchiveInfo(downloadLocation, 100 PackageManager.GET_SIGNATURES | PackageManager.GET_RECEIVERS); 101 String packageName = mProvisioningParams.inferDeviceAdminPackageName(); 102 // Device admin package name can't be null 103 if (packageInfo == null || packageName == null) { 104 ProvisionLogger.loge("Device admin package info or name is null"); 105 error(ERROR_DEVICE_ADMIN_MISSING); 106 return; 107 } 108 109 if (mUtils.findDeviceAdminInPackageInfo(packageName, 110 mProvisioningParams.deviceAdminComponentName, packageInfo) == null) { 111 error(ERROR_DEVICE_ADMIN_MISSING); 112 return; 113 } 114 115 if (mDownloadInfo.packageChecksum.length > 0) { 116 if (!doesPackageHashMatch(downloadLocation, mDownloadInfo.packageChecksum)) { 117 error(ERROR_HASH_MISMATCH); 118 return; 119 } 120 } else { 121 if (!doesASignatureHashMatch(packageInfo, mDownloadInfo.signatureChecksum)) { 122 error(ERROR_HASH_MISMATCH); 123 return; 124 } 125 } 126 127 success(); 128 } 129 130 @Override getStatusMsgId()131 public int getStatusMsgId() { 132 return R.string.progress_install; 133 } 134 computeHashesOfAllSignatures(Signature[] signatures)135 private List<byte[]> computeHashesOfAllSignatures(Signature[] signatures) { 136 if (signatures == null) { 137 return null; 138 } 139 140 List<byte[]> hashes = new LinkedList<>(); 141 for (Signature signature : signatures) { 142 byte[] hash = mUtils.computeHashOfByteArray(signature.toByteArray()); 143 hashes.add(hash); 144 } 145 return hashes; 146 } 147 doesASignatureHashMatch(PackageInfo packageInfo, byte[] signatureChecksum)148 private boolean doesASignatureHashMatch(PackageInfo packageInfo, byte[] signatureChecksum) { 149 // Check whether a signature hash of downloaded apk matches the hash given in constructor. 150 ProvisionLogger.logd("Checking " + Utils.SHA256_TYPE 151 + "-hashes of all signatures of downloaded package."); 152 List<byte[]> sigHashes = computeHashesOfAllSignatures(packageInfo.signatures); 153 if (sigHashes == null || sigHashes.isEmpty()) { 154 ProvisionLogger.loge("Downloaded package does not have any signatures."); 155 return false; 156 } 157 158 for (byte[] sigHash : sigHashes) { 159 if (Arrays.equals(sigHash, signatureChecksum)) { 160 return true; 161 } 162 } 163 164 ProvisionLogger.loge("Provided hash does not match any signature hash."); 165 ProvisionLogger.loge("Hash provided by programmer: " 166 + StoreUtils.byteArrayToString(signatureChecksum)); 167 ProvisionLogger.loge("Hashes computed from package signatures: "); 168 for (byte[] sigHash : sigHashes) { 169 if (sigHash != null) { 170 ProvisionLogger.loge(StoreUtils.byteArrayToString(sigHash)); 171 } 172 } 173 174 return false; 175 } 176 177 /** 178 * Check whether package hash of downloaded file matches the hash given in PackageDownloadInfo. 179 * By default, SHA-256 is used to verify the file hash. 180 */ doesPackageHashMatch(String downloadLocation, byte[] packageChecksum)181 private boolean doesPackageHashMatch(String downloadLocation, byte[] packageChecksum) { 182 byte[] packageSha256Hash = null; 183 184 ProvisionLogger.logd("Checking file hash of entire apk file."); 185 packageSha256Hash = mUtils.computeHashOfFile(downloadLocation, Utils.SHA256_TYPE); 186 if (Arrays.equals(packageChecksum, packageSha256Hash)) { 187 return true; 188 } 189 190 ProvisionLogger.loge("Provided hash does not match file hash."); 191 ProvisionLogger.loge("Hash provided by programmer: " 192 + StoreUtils.byteArrayToString(packageChecksum)); 193 if (packageSha256Hash != null) { 194 ProvisionLogger.loge("SHA-256 Hash computed from file: " 195 + StoreUtils.byteArrayToString(packageSha256Hash)); 196 } 197 return false; 198 } 199 } 200