• 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;
18 
19 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
20 
21 import android.content.Context;
22 import android.content.pm.UserInfo;
23 import android.os.Environment;
24 import android.os.FileUtils;
25 import android.os.RecoverySystem;
26 import android.os.storage.StorageManager;
27 import android.os.storage.VolumeInfo;
28 import android.os.SystemProperties;
29 import android.os.UserHandle;
30 import android.system.ErrnoException;
31 import android.system.Os;
32 import android.system.OsConstants;
33 import android.util.Log;
34 import android.util.Slog;
35 import android.util.SparseArray;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 
39 import java.io.File;
40 import java.io.IOException;
41 import java.nio.charset.StandardCharsets;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Objects;
46 import java.util.Set;
47 
48 /**
49  * Helper class for preparing and destroying user storage
50  */
51 class UserDataPreparer {
52     private static final String TAG = "UserDataPreparer";
53     private static final String XATTR_SERIAL = "user.serial";
54 
55     private final Object mInstallLock;
56     private final Context mContext;
57     private final boolean mOnlyCore;
58     private final Installer mInstaller;
59 
UserDataPreparer(Installer installer, Object installLock, Context context, boolean onlyCore)60     UserDataPreparer(Installer installer, Object installLock, Context context, boolean onlyCore) {
61         mInstallLock = installLock;
62         mContext = context;
63         mOnlyCore = onlyCore;
64         mInstaller = installer;
65     }
66 
67     /**
68      * Prepare storage areas for given user on all mounted devices.
69      */
prepareUserData(int userId, int userSerial, int flags)70     void prepareUserData(int userId, int userSerial, int flags) {
71         synchronized (mInstallLock) {
72             final StorageManager storage = mContext.getSystemService(StorageManager.class);
73             /*
74              * Internal storage must be prepared before adoptable storage, since the user's volume
75              * keys are stored in their internal storage.
76              */
77             prepareUserDataLI(null /* internal storage */, userId, userSerial, flags, true);
78             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
79                 final String volumeUuid = vol.getFsUuid();
80                 if (volumeUuid != null) {
81                     prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
82                 }
83             }
84         }
85     }
86 
prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags, boolean allowRecover)87     private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
88             boolean allowRecover) {
89         // Prepare storage and verify that serial numbers are consistent; if
90         // there's a mismatch we need to destroy to avoid leaking data
91         final StorageManager storage = mContext.getSystemService(StorageManager.class);
92         try {
93             storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
94 
95             if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
96                 enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
97                 if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
98                     enforceSerialNumber(getDataSystemDeDirectory(userId), userSerial);
99                 }
100             }
101             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
102                 enforceSerialNumber(getDataUserCeDirectory(volumeUuid, userId), userSerial);
103                 if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
104                     enforceSerialNumber(getDataSystemCeDirectory(userId), userSerial);
105                 }
106             }
107 
108             mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
109 
110             // CE storage is available after they are prepared.
111             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 &&
112                     (userId == UserHandle.USER_SYSTEM)) {
113                 String propertyName = "sys.user." + userId + ".ce_available";
114                 Slog.d(TAG, "Setting property: " + propertyName + "=true");
115                 SystemProperties.set(propertyName, "true");
116             }
117         } catch (Exception e) {
118             logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
119                     + " because we failed to prepare: " + e);
120             destroyUserDataLI(volumeUuid, userId, flags);
121 
122             if (allowRecover) {
123                 // Try one last time; if we fail again we're really in trouble
124                 prepareUserDataLI(volumeUuid, userId, userSerial,
125                     flags | StorageManager.FLAG_STORAGE_DE, false);
126             } else {
127                 try {
128                     Log.wtf(TAG, "prepareUserData failed for user " + userId, e);
129                     if (userId == UserHandle.USER_SYSTEM) {
130                         RecoverySystem.rebootPromptAndWipeUserData(mContext,
131                                 "prepareUserData failed for system user");
132                     }
133                 } catch (IOException e2) {
134                     throw new RuntimeException("error rebooting into recovery", e2);
135                 }
136             }
137         }
138     }
139 
140     /**
141      * Destroy storage areas for given user on all mounted devices.
142      */
destroyUserData(int userId, int flags)143     void destroyUserData(int userId, int flags) {
144         synchronized (mInstallLock) {
145             final StorageManager storage = mContext.getSystemService(StorageManager.class);
146             /*
147              * Volume destruction order isn't really important, but to avoid any weird issues we
148              * process internal storage last, the opposite of prepareUserData.
149              */
150             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
151                 final String volumeUuid = vol.getFsUuid();
152                 if (volumeUuid != null) {
153                     destroyUserDataLI(volumeUuid, userId, flags);
154                 }
155             }
156             destroyUserDataLI(null /* internal storage */, userId, flags);
157         }
158     }
159 
destroyUserDataLI(String volumeUuid, int userId, int flags)160     void destroyUserDataLI(String volumeUuid, int userId, int flags) {
161         final StorageManager storage = mContext.getSystemService(StorageManager.class);
162         try {
163             // Clean up app data, profile data, and media data
164             mInstaller.destroyUserData(volumeUuid, userId, flags);
165 
166             // Clean up system data
167             if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
168                 if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
169                     FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId));
170                     FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId));
171                 }
172                 if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
173                     FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId));
174                 }
175             }
176 
177             // Data with special labels is now gone, so finish the job
178             storage.destroyUserStorage(volumeUuid, userId, flags);
179 
180         } catch (Exception e) {
181             logCriticalInfo(Log.WARN,
182                     "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
183         }
184     }
185 
186     /**
187      * Examine all users present on given mounted volume, and destroy data
188      * belonging to users that are no longer valid, or whose user ID has been
189      * recycled.
190      */
reconcileUsers(String volumeUuid, List<UserInfo> validUsersList)191     void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList) {
192         final List<File> files = new ArrayList<>();
193         Collections.addAll(files, FileUtils
194                 .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
195         Collections.addAll(files, FileUtils
196                 .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
197         Collections.addAll(files, FileUtils
198                 .listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
199         Collections.addAll(files, FileUtils
200                 .listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
201         Collections.addAll(files, FileUtils
202                 .listFilesOrEmpty(Environment.getDataMiscCeDirectory()));
203         reconcileUsers(volumeUuid, validUsersList, files);
204     }
205 
206     @VisibleForTesting
reconcileUsers(String volumeUuid, List<UserInfo> validUsersList, List<File> files)207     void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList, List<File> files) {
208         final int userCount = validUsersList.size();
209         SparseArray<UserInfo> users = new SparseArray<>(userCount);
210         for (int i = 0; i < userCount; i++) {
211             UserInfo user = validUsersList.get(i);
212             users.put(user.id, user);
213         }
214         for (File file : files) {
215             if (!file.isDirectory()) {
216                 continue;
217             }
218 
219             final int userId;
220             final UserInfo info;
221             try {
222                 userId = Integer.parseInt(file.getName());
223                 info = users.get(userId);
224             } catch (NumberFormatException e) {
225                 Slog.w(TAG, "Invalid user directory " + file);
226                 continue;
227             }
228 
229             boolean destroyUser = false;
230             if (info == null) {
231                 logCriticalInfo(Log.WARN, "Destroying user directory " + file
232                         + " because no matching user was found");
233                 destroyUser = true;
234             } else if (!mOnlyCore) {
235                 try {
236                     enforceSerialNumber(file, info.serialNumber);
237                 } catch (IOException e) {
238                     logCriticalInfo(Log.WARN, "Destroying user directory " + file
239                             + " because we failed to enforce serial number: " + e);
240                     destroyUser = true;
241                 }
242             }
243 
244             if (destroyUser) {
245                 synchronized (mInstallLock) {
246                     destroyUserDataLI(volumeUuid, userId,
247                             StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
248                 }
249             }
250         }
251     }
252 
253     @VisibleForTesting
getDataMiscCeDirectory(int userId)254     protected File getDataMiscCeDirectory(int userId) {
255         return Environment.getDataMiscCeDirectory(userId);
256     }
257 
258     @VisibleForTesting
getDataSystemCeDirectory(int userId)259     protected File getDataSystemCeDirectory(int userId) {
260         return Environment.getDataSystemCeDirectory(userId);
261     }
262 
263     @VisibleForTesting
getDataMiscDeDirectory(int userId)264     protected File getDataMiscDeDirectory(int userId) {
265         return Environment.getDataMiscDeDirectory(userId);
266     }
267 
268     @VisibleForTesting
getUserSystemDirectory(int userId)269     protected File getUserSystemDirectory(int userId) {
270         return Environment.getUserSystemDirectory(userId);
271     }
272 
273     @VisibleForTesting
getDataUserCeDirectory(String volumeUuid, int userId)274     protected File getDataUserCeDirectory(String volumeUuid, int userId) {
275         return Environment.getDataUserCeDirectory(volumeUuid, userId);
276     }
277 
278     @VisibleForTesting
getDataSystemDeDirectory(int userId)279     protected File getDataSystemDeDirectory(int userId) {
280         return Environment.getDataSystemDeDirectory(userId);
281     }
282 
283     @VisibleForTesting
getDataUserDeDirectory(String volumeUuid, int userId)284     protected File getDataUserDeDirectory(String volumeUuid, int userId) {
285         return Environment.getDataUserDeDirectory(volumeUuid, userId);
286     }
287 
288     @VisibleForTesting
isFileEncryptedEmulatedOnly()289     protected boolean isFileEncryptedEmulatedOnly() {
290         return StorageManager.isFileEncryptedEmulatedOnly();
291     }
292 
293     /**
294      * Enforce that serial number stored in user directory inode matches the
295      * given expected value. Gracefully sets the serial number if currently
296      * undefined.
297      *
298      * @throws IOException when problem extracting serial number, or serial
299      *             number is mismatched.
300      */
enforceSerialNumber(File file, int serialNumber)301     void enforceSerialNumber(File file, int serialNumber) throws IOException {
302         if (isFileEncryptedEmulatedOnly()) {
303             // When we're emulating FBE, the directory may have been chmod
304             // 000'ed, meaning we can't read the serial number to enforce it;
305             // instead of destroying the user, just log a warning.
306             Slog.w(TAG, "Device is emulating FBE; assuming current serial number is valid");
307             return;
308         }
309 
310         final int foundSerial = getSerialNumber(file);
311         Slog.v(TAG, "Found " + file + " with serial number " + foundSerial);
312 
313         if (foundSerial == -1) {
314             Slog.d(TAG, "Serial number missing on " + file + "; assuming current is valid");
315             try {
316                 setSerialNumber(file, serialNumber);
317             } catch (IOException e) {
318                 Slog.w(TAG, "Failed to set serial number on " + file, e);
319             }
320 
321         } else if (foundSerial != serialNumber) {
322             throw new IOException("Found serial number " + foundSerial
323                     + " doesn't match expected " + serialNumber);
324         }
325     }
326 
327     /**
328      * Set serial number stored in user directory inode.
329      *
330      * @throws IOException if serial number was already set
331      */
setSerialNumber(File file, int serialNumber)332     private static void setSerialNumber(File file, int serialNumber) throws IOException {
333         try {
334             final byte[] buf = Integer.toString(serialNumber).getBytes(StandardCharsets.UTF_8);
335             Os.setxattr(file.getAbsolutePath(), XATTR_SERIAL, buf, OsConstants.XATTR_CREATE);
336         } catch (ErrnoException e) {
337             throw e.rethrowAsIOException();
338         }
339     }
340 
341     /**
342      * Return serial number stored in user directory inode.
343      *
344      * @return parsed serial number, or -1 if not set
345      */
346     @VisibleForTesting
getSerialNumber(File file)347     static int getSerialNumber(File file) throws IOException {
348         try {
349             final byte[] buf = Os.getxattr(file.getAbsolutePath(), XATTR_SERIAL);
350             final String serial = new String(buf);
351             try {
352                 return Integer.parseInt(serial);
353             } catch (NumberFormatException e) {
354                 throw new IOException("Bad serial number: " + serial);
355             }
356         } catch (ErrnoException e) {
357             if (e.errno == OsConstants.ENODATA) {
358                 return -1;
359             } else {
360                 throw e.rethrowAsIOException();
361             }
362         }
363     }
364 
365 }
366