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