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