• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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