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