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