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