Merge "Invoke BLKSECDISCARD to securely delete sensitive data" into oc-dev
am: 28f0fd7a2d
Change-Id: I9e0ffce82703793004b6900db39ad7b5c5500ec3
This commit is contained in:
@@ -295,4 +295,5 @@ interface IStorageManager {
|
||||
long getCacheSizeBytes(String volumeUuid, int uid) = 76;
|
||||
long getAllocatableBytes(String volumeUuid, int flags) = 77;
|
||||
void allocateBytes(String volumeUuid, long bytes, int flags) = 78;
|
||||
void secdiscard(in String path) = 79;
|
||||
}
|
||||
|
||||
@@ -1251,6 +1251,15 @@ public class StorageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public void secdiscard(String path) {
|
||||
try {
|
||||
mStorageManager.secdiscard(path);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public static boolean isUserKeyUnlocked(int userId) {
|
||||
if (sStorageManager == null) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
@@ -336,8 +337,11 @@ class LockSettingsStorage {
|
||||
synchronized (mFileWriteLock) {
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
// Write the hash to file
|
||||
raf = new RandomAccessFile(name, "rw");
|
||||
// Write the hash to file, requiring each write to be synchronized to the
|
||||
// underlying storage device immediately to avoid data loss in case of power loss.
|
||||
// This also ensures future secdiscard operation on the file succeeds since the
|
||||
// file would have been allocated on flash.
|
||||
raf = new RandomAccessFile(name, "rws");
|
||||
// Truncate the file if pattern is null, to clear the lock
|
||||
if (hash == null || hash.length == 0) {
|
||||
raf.setLength(0);
|
||||
@@ -432,12 +436,17 @@ class LockSettingsStorage {
|
||||
return readFile(getSynthenticPasswordStateFilePathForUser(userId, handle, name));
|
||||
}
|
||||
|
||||
public void deleteSyntheticPasswordState(int userId, long handle, String name, boolean secure) {
|
||||
public void deleteSyntheticPasswordState(int userId, long handle, String name) {
|
||||
String path = getSynthenticPasswordStateFilePathForUser(userId, handle, name);
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
//TODO: (b/34600579) invoke secdiscardable
|
||||
file.delete();
|
||||
try {
|
||||
mContext.getSystemService(StorageManager.class).secdiscard(file.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Failed to secdiscard " + path, e);
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
mCache.putFile(path, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3009,6 +3009,18 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void secdiscard(String path) {
|
||||
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
|
||||
waitForReady();
|
||||
|
||||
try {
|
||||
mCryptConnector.execute("cryptfs", "secdiscard", escapeNull(path));
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
throw e.rethrowAsParcelableException();
|
||||
}
|
||||
}
|
||||
|
||||
class AppFuseMountScope extends AppFuseBridge.MountScope {
|
||||
boolean opened = false;
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ public class SyntheticPasswordManager {
|
||||
|
||||
// Nuke the SP handle (and as a result, its SID) for the given user.
|
||||
public void clearSidForUser(int userId) {
|
||||
destroyState(SP_HANDLE_NAME, true, DEFAULT_HANDLE, userId);
|
||||
destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
|
||||
}
|
||||
|
||||
public boolean hasSidForUser(int userId) {
|
||||
@@ -318,8 +318,8 @@ public class SyntheticPasswordManager {
|
||||
}
|
||||
|
||||
public void destroyEscrowData(int userId) {
|
||||
destroyState(SP_E0_NAME, true, DEFAULT_HANDLE, userId);
|
||||
destroyState(SP_P1_NAME, true, DEFAULT_HANDLE, userId);
|
||||
destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
|
||||
destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,17 +584,17 @@ public class SyntheticPasswordManager {
|
||||
|
||||
public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
|
||||
destroySyntheticPassword(handle, userId);
|
||||
destroyState(SECDISCARDABLE_NAME, true, handle, userId);
|
||||
destroyState(SECDISCARDABLE_NAME, handle, userId);
|
||||
}
|
||||
|
||||
public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
|
||||
destroySyntheticPassword(handle, userId);
|
||||
destroyState(SECDISCARDABLE_NAME, true, handle, userId);
|
||||
destroyState(PASSWORD_DATA_NAME, true, handle, userId);
|
||||
destroyState(SECDISCARDABLE_NAME, handle, userId);
|
||||
destroyState(PASSWORD_DATA_NAME, handle, userId);
|
||||
}
|
||||
|
||||
private void destroySyntheticPassword(long handle, int userId) {
|
||||
destroyState(SP_BLOB_NAME, true, handle, userId);
|
||||
destroyState(SP_BLOB_NAME, handle, userId);
|
||||
destroySPBlobKey(getHandleName(handle));
|
||||
}
|
||||
|
||||
@@ -629,8 +629,8 @@ public class SyntheticPasswordManager {
|
||||
mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
|
||||
}
|
||||
|
||||
private void destroyState(String stateName, boolean secure, long handle, int userId) {
|
||||
mStorage.deleteSyntheticPasswordState(userId, handle, stateName, secure);
|
||||
private void destroyState(String stateName, long handle, int userId) {
|
||||
mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
|
||||
}
|
||||
|
||||
protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.pm.UserInfo;
|
||||
import android.os.FileUtils;
|
||||
import android.os.IProgressListener;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.security.KeyStore;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
@@ -82,7 +83,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
mActivityManager = mock(IActivityManager.class);
|
||||
mDevicePolicyManager = mock(DevicePolicyManager.class);
|
||||
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
|
||||
mDevicePolicyManager);
|
||||
mDevicePolicyManager, mock(StorageManager.class));
|
||||
mStorage = new LockSettingsStorageTestable(mContext,
|
||||
new File(getContext().getFilesDir(), "locksettings"));
|
||||
File storageDir = mStorage.mStorageDir;
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.pm.UserInfo;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.FileUtils;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
@@ -69,7 +70,8 @@ public class LockSettingsStorageTests extends AndroidTestCase {
|
||||
when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
|
||||
|
||||
MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
|
||||
mock(NotificationManager.class), mock(DevicePolicyManager.class));
|
||||
mock(NotificationManager.class), mock(DevicePolicyManager.class),
|
||||
mock(StorageManager.class));
|
||||
mStorage = new LockSettingsStorageTestable(context,
|
||||
new File(getContext().getFilesDir(), "locksettings"));
|
||||
mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
|
||||
@@ -336,7 +338,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
|
||||
assertArrayEquals(data, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
|
||||
assertEquals(null, mStorage.readSyntheticPasswordState(0, 1234L, "state"));
|
||||
|
||||
mStorage.deleteSyntheticPasswordState(10, 1234L, "state", true);
|
||||
mStorage.deleteSyntheticPasswordState(10, 1234L, "state");
|
||||
assertEquals(null, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,19 +21,23 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
|
||||
public class MockLockSettingsContext extends ContextWrapper {
|
||||
|
||||
private UserManager mUserManager;
|
||||
private NotificationManager mNotificationManager;
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
private StorageManager mStorageManager;
|
||||
|
||||
public MockLockSettingsContext(Context base, UserManager userManager,
|
||||
NotificationManager notificationManager, DevicePolicyManager devicePolicyManager) {
|
||||
NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
|
||||
StorageManager storageManager) {
|
||||
super(base);
|
||||
mUserManager = userManager;
|
||||
mNotificationManager = notificationManager;
|
||||
mDevicePolicyManager = devicePolicyManager;
|
||||
mStorageManager = storageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,6 +48,8 @@ public class MockLockSettingsContext extends ContextWrapper {
|
||||
return mNotificationManager;
|
||||
} else if (DEVICE_POLICY_SERVICE.equals(name)) {
|
||||
return mDevicePolicyManager;
|
||||
} else if (STORAGE_SERVICE.equals(name)) {
|
||||
return mStorageManager;
|
||||
} else {
|
||||
throw new RuntimeException("System service not mocked: " + name);
|
||||
}
|
||||
|
||||
@@ -500,4 +500,9 @@ public class MockStorageManager implements IStorageManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void secdiscard(String path) throws RemoteException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user