• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.server.pm.dex;
18 
19 import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
20 import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_NATIVE;
21 
22 import android.annotation.NonNull;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.IPackageManager;
25 import android.content.pm.PackageInfo;
26 import android.os.FileUtils;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.os.UserHandle;
30 import android.os.storage.StorageManager;
31 import android.util.EventLog;
32 import android.util.PackageUtils;
33 import android.util.Slog;
34 import android.util.SparseArray;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.server.pm.Installer;
38 import com.android.server.pm.Installer.InstallerException;
39 import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
40 import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
41 
42 import libcore.util.HexEncoding;
43 
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.Map;
47 import java.util.Set;
48 
49 /**
50  * This class is responsible for logging data about secondary dex files and native code executed
51  * from an app's private directory. The data logged includes hashes of the name and content of each
52  * file.
53  */
54 public class DynamicCodeLogger {
55     private static final String TAG = "DynamicCodeLogger";
56 
57     // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) -
58     // see b/63927552.
59     private static final int SNET_TAG = 0x534e4554;
60     private static final String DCL_DEX_SUBTAG = "dcl";
61     private static final String DCL_NATIVE_SUBTAG = "dcln";
62 
63     private IPackageManager mPackageManager;
64     private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
65     private final Installer mInstaller;
66 
DynamicCodeLogger(Installer installer)67     DynamicCodeLogger(Installer installer) {
68         mInstaller = installer;
69         mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
70     }
71 
72     @VisibleForTesting
DynamicCodeLogger(@onNull IPackageManager packageManager, @NonNull Installer installer, @NonNull PackageDynamicCodeLoading packageDynamicCodeLoading)73     DynamicCodeLogger(@NonNull IPackageManager packageManager, @NonNull Installer installer,
74             @NonNull PackageDynamicCodeLoading packageDynamicCodeLoading) {
75         mPackageManager = packageManager;
76         mInstaller = installer;
77         mPackageDynamicCodeLoading = packageDynamicCodeLoading;
78     }
79 
80     @NonNull
getPackageManager()81     private IPackageManager getPackageManager() {
82         if (mPackageManager == null) {
83             mPackageManager = IPackageManager.Stub.asInterface(
84                     ServiceManager.getService("package"));
85         }
86         return mPackageManager;
87     }
88 
getAllPackagesWithDynamicCodeLoading()89     public Set<String> getAllPackagesWithDynamicCodeLoading() {
90         return mPackageDynamicCodeLoading.getAllPackagesWithDynamicCodeLoading();
91     }
92 
93     /**
94      * Write information about code dynamically loaded by {@code packageName} to the event log.
95      */
logDynamicCodeLoading(String packageName)96     public void logDynamicCodeLoading(String packageName) {
97         PackageDynamicCode info = getPackageDynamicCodeInfo(packageName);
98         if (info == null) {
99             return;
100         }
101 
102         SparseArray<ApplicationInfo> appInfoByUser = new SparseArray<>();
103         boolean needWrite = false;
104 
105         for (Map.Entry<String, DynamicCodeFile> fileEntry : info.mFileUsageMap.entrySet()) {
106             String filePath = fileEntry.getKey();
107             DynamicCodeFile fileInfo = fileEntry.getValue();
108             int userId = fileInfo.mUserId;
109 
110             int index = appInfoByUser.indexOfKey(userId);
111             ApplicationInfo appInfo;
112             if (index >= 0) {
113                 appInfo = appInfoByUser.get(userId);
114             } else {
115                 appInfo = null;
116 
117                 try {
118                     PackageInfo ownerInfo =
119                             getPackageManager().getPackageInfo(packageName, /*flags*/ 0, userId);
120                     appInfo = ownerInfo == null ? null : ownerInfo.applicationInfo;
121                 } catch (RemoteException ignored) {
122                     // Can't happen, we're local.
123                 }
124                 appInfoByUser.put(userId, appInfo);
125                 if (appInfo == null) {
126                     Slog.d(TAG, "Could not find package " + packageName + " for user " + userId);
127                     // Package has probably been uninstalled for user.
128                     needWrite |= mPackageDynamicCodeLoading.removeUserPackage(packageName, userId);
129                 }
130             }
131 
132             if (appInfo == null) {
133                 continue;
134             }
135 
136             int storageFlags;
137 
138             if (fileIsUnder(filePath, appInfo.credentialProtectedDataDir)) {
139                 storageFlags = StorageManager.FLAG_STORAGE_CE;
140             } else if (fileIsUnder(filePath, appInfo.deviceProtectedDataDir)) {
141                 storageFlags = StorageManager.FLAG_STORAGE_DE;
142             } else {
143                 Slog.e(TAG, "Could not infer CE/DE storage for path " + filePath);
144                 needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
145                 continue;
146             }
147 
148             byte[] hash = null;
149             try {
150                 // Note that we do not take the install lock here. Hashing should never interfere
151                 // with app update/compilation/removal. We may get anomalous results if a file
152                 // changes while we hash it, but that can happen anyway and is harmless for our
153                 // purposes.
154                 hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
155                         appInfo.volumeUuid, storageFlags);
156             } catch (InstallerException e) {
157                 Slog.e(TAG, "Got InstallerException when hashing file " + filePath
158                         + ": " + e.getMessage());
159             }
160 
161             String subtag = fileInfo.mFileType == FILE_TYPE_DEX
162                     ? DCL_DEX_SUBTAG
163                     : DCL_NATIVE_SUBTAG;
164             String fileName = new File(filePath).getName();
165             String message = PackageUtils.computeSha256Digest(fileName.getBytes());
166 
167             // Valid SHA256 will be 256 bits, 32 bytes.
168             if (hash != null && hash.length == 32) {
169                 message = message + ' ' + HexEncoding.encodeToString(hash);
170             } else {
171                 Slog.d(TAG, "Got no hash for " + filePath);
172                 // File has probably been deleted.
173                 needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
174             }
175 
176             for (String loadingPackageName : fileInfo.mLoadingPackages) {
177                 int loadingUid = -1;
178                 if (loadingPackageName.equals(packageName)) {
179                     loadingUid = appInfo.uid;
180                 } else {
181                     try {
182                         loadingUid =  getPackageManager().getPackageUid(loadingPackageName, /*flags*/ 0,
183                                 userId);
184                     } catch (RemoteException ignored) {
185                         // Can't happen, we're local.
186                     }
187                 }
188 
189                 if (loadingUid != -1) {
190                     writeDclEvent(subtag, loadingUid, message);
191                 }
192             }
193         }
194 
195         if (needWrite) {
196             mPackageDynamicCodeLoading.maybeWriteAsync();
197         }
198     }
199 
fileIsUnder(String filePath, String directoryPath)200     private boolean fileIsUnder(String filePath, String directoryPath) {
201         if (directoryPath == null) {
202             return false;
203         }
204 
205         try {
206             return FileUtils.contains(new File(directoryPath).getCanonicalPath(),
207                     new File(filePath).getCanonicalPath());
208         } catch (IOException e) {
209             return false;
210         }
211     }
212 
213     @VisibleForTesting
getPackageDynamicCodeInfo(String packageName)214     PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
215         return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
216     }
217 
218     @VisibleForTesting
writeDclEvent(String subtag, int uid, String message)219     void writeDclEvent(String subtag, int uid, String message) {
220         EventLog.writeEvent(SNET_TAG, subtag, uid, message);
221     }
222 
recordDex(int loaderUserId, String dexPath, String owningPackageName, String loadingPackageName)223     void recordDex(int loaderUserId, String dexPath, String owningPackageName,
224             String loadingPackageName) {
225         if (mPackageDynamicCodeLoading.record(owningPackageName, dexPath,
226                 FILE_TYPE_DEX, loaderUserId, loadingPackageName)) {
227             mPackageDynamicCodeLoading.maybeWriteAsync();
228         }
229     }
230 
231     /**
232      * Record that an app running in the specified uid has executed native code from the file at
233      * {@param path}.
234      */
recordNative(int loadingUid, String path)235     public void recordNative(int loadingUid, String path) {
236         String[] packages;
237         try {
238             packages =  getPackageManager().getPackagesForUid(loadingUid);
239             if (packages == null || packages.length == 0) {
240                 return;
241             }
242         } catch (RemoteException e) {
243             // Can't happen, we're local.
244             return;
245         }
246 
247         String loadingPackageName = packages[0];
248         int loadingUserId = UserHandle.getUserId(loadingUid);
249 
250         if (mPackageDynamicCodeLoading.record(loadingPackageName, path,
251                 FILE_TYPE_NATIVE, loadingUserId, loadingPackageName)) {
252             mPackageDynamicCodeLoading.maybeWriteAsync();
253         }
254     }
255 
clear()256     void clear() {
257         mPackageDynamicCodeLoading.clear();
258     }
259 
removePackage(String packageName)260     void removePackage(String packageName) {
261         if (mPackageDynamicCodeLoading.removePackage(packageName)) {
262             mPackageDynamicCodeLoading.maybeWriteAsync();
263         }
264     }
265 
removeUserPackage(String packageName, int userId)266     void removeUserPackage(String packageName, int userId) {
267         if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
268             mPackageDynamicCodeLoading.maybeWriteAsync();
269         }
270     }
271 
readAndSync(Map<String, Set<Integer>> packageToUsersMap)272     void readAndSync(Map<String, Set<Integer>> packageToUsersMap) {
273         mPackageDynamicCodeLoading.read();
274         mPackageDynamicCodeLoading.syncData(packageToUsersMap);
275     }
276 
writeNow()277     void writeNow() {
278         mPackageDynamicCodeLoading.writeNow();
279     }
280 }
281