• 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.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