Merge "Add a checksum to FRP partition" into lmp-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6722598ef3
@@ -41,6 +41,9 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for reading and writing blocks to a persistent partition.
|
* Service for reading and writing blocks to a persistent partition.
|
||||||
@@ -63,22 +66,16 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
|
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
|
||||||
private static final int HEADER_SIZE = 8;
|
private static final int HEADER_SIZE = 8;
|
||||||
// Magic number to mark block device as adhering to the format consumed by this service
|
// Magic number to mark block device as adhering to the format consumed by this service
|
||||||
private static final int PARTITION_TYPE_MARKER = 0x1990;
|
private static final int PARTITION_TYPE_MARKER = 0x19901873;
|
||||||
// Limit to 100k as blocks larger than this might cause strain on Binder.
|
// Limit to 100k as blocks larger than this might cause strain on Binder.
|
||||||
// TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager
|
|
||||||
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
|
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
|
||||||
|
public static final int DIGEST_SIZE_BYTES = 32;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final String mDataBlockFile;
|
private final String mDataBlockFile;
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
private int mAllowedUid = -1;
|
private int mAllowedUid = -1;
|
||||||
/*
|
|
||||||
* Separate lock for OEM unlock related operations as they can happen in parallel with regular
|
|
||||||
* block operations.
|
|
||||||
*/
|
|
||||||
private final Object mOemLock = new Object();
|
|
||||||
|
|
||||||
private long mBlockDeviceSize;
|
private long mBlockDeviceSize;
|
||||||
|
|
||||||
public PersistentDataBlockService(Context context) {
|
public PersistentDataBlockService(Context context) {
|
||||||
@@ -89,7 +86,6 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
|
mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int getAllowedUid(int userHandle) {
|
private int getAllowedUid(int userHandle) {
|
||||||
String allowedPackage = mContext.getResources()
|
String allowedPackage = mContext.getResources()
|
||||||
.getString(R.string.config_persistentDataPackageName);
|
.getString(R.string.config_persistentDataPackageName);
|
||||||
@@ -106,6 +102,7 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
|
enforceChecksumValidity();
|
||||||
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
|
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +125,9 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
|
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
|
||||||
|
// skip over checksum
|
||||||
|
inputStream.skipBytes(DIGEST_SIZE_BYTES);
|
||||||
|
|
||||||
int totalDataSize;
|
int totalDataSize;
|
||||||
int blockId = inputStream.readInt();
|
int blockId = inputStream.readInt();
|
||||||
if (blockId == PARTITION_TYPE_MARKER) {
|
if (blockId == PARTITION_TYPE_MARKER) {
|
||||||
@@ -148,6 +148,143 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
return mBlockDeviceSize;
|
return mBlockDeviceSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean enforceChecksumValidity() {
|
||||||
|
byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
|
||||||
|
|
||||||
|
synchronized (mLock) {
|
||||||
|
byte[] digest = computeDigestLocked(storedDigest);
|
||||||
|
if (digest == null || !Arrays.equals(storedDigest, digest)) {
|
||||||
|
Slog.i(TAG, "Formatting FRP partition...");
|
||||||
|
formatPartitionLocked();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean computeAndWriteDigestLocked() {
|
||||||
|
byte[] digest = computeDigestLocked(null);
|
||||||
|
if (digest != null) {
|
||||||
|
DataOutputStream outputStream;
|
||||||
|
try {
|
||||||
|
outputStream = new DataOutputStream(
|
||||||
|
new FileOutputStream(new File(mDataBlockFile)));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Slog.e(TAG, "partition not available?", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "failed to write block checksum", e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(outputStream);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] computeDigestLocked(byte[] storedDigest) {
|
||||||
|
DataInputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Slog.e(TAG, "partition not available?", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDigest md;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-256");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
// won't ever happen -- every implementation is required to support SHA-256
|
||||||
|
Slog.e(TAG, "SHA-256 not supported?", e);
|
||||||
|
IoUtils.closeQuietly(inputStream);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
|
||||||
|
inputStream.read(storedDigest);
|
||||||
|
} else {
|
||||||
|
inputStream.skipBytes(DIGEST_SIZE_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
int read;
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
|
||||||
|
while ((read = inputStream.read(data)) != -1) {
|
||||||
|
md.update(data, 0, read);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "failed to read partition", e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return md.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatPartitionLocked() {
|
||||||
|
DataOutputStream outputStream;
|
||||||
|
try {
|
||||||
|
outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Slog.e(TAG, "partition not available?", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = new byte[DIGEST_SIZE_BYTES];
|
||||||
|
try {
|
||||||
|
outputStream.write(data, 0, DIGEST_SIZE_BYTES);
|
||||||
|
outputStream.writeInt(PARTITION_TYPE_MARKER);
|
||||||
|
outputStream.writeInt(0); // data size
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "failed to format block", e);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
doSetOemUnlockEnabledLocked(false);
|
||||||
|
computeAndWriteDigestLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSetOemUnlockEnabledLocked(boolean enabled) {
|
||||||
|
FileOutputStream outputStream;
|
||||||
|
try {
|
||||||
|
outputStream = new FileOutputStream(new File(mDataBlockFile));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Slog.e(TAG, "partition not available", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileChannel channel = outputStream.getChannel();
|
||||||
|
|
||||||
|
channel.position(getBlockDeviceSize() - 1);
|
||||||
|
|
||||||
|
ByteBuffer data = ByteBuffer.allocate(1);
|
||||||
|
data.put(enabled ? (byte) 1 : (byte) 0);
|
||||||
|
data.flip();
|
||||||
|
channel.write(data);
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "unable to access persistent partition", e);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private native long nativeGetBlockDeviceSize(String path);
|
private native long nativeGetBlockDeviceSize(String path);
|
||||||
private native int nativeWipe(String path);
|
private native int nativeWipe(String path);
|
||||||
|
|
||||||
@@ -176,19 +313,23 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
headerAndData.putInt(data.length);
|
headerAndData.putInt(data.length);
|
||||||
headerAndData.put(data);
|
headerAndData.put(data);
|
||||||
|
|
||||||
try {
|
synchronized (mLock) {
|
||||||
synchronized (mLock) {
|
|
||||||
outputStream.write(headerAndData.array());
|
|
||||||
return data.length;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Slog.e(TAG, "failed writing to the persistent data block", e);
|
|
||||||
return -1;
|
|
||||||
} finally {
|
|
||||||
try {
|
try {
|
||||||
outputStream.close();
|
byte[] checksum = new byte[DIGEST_SIZE_BYTES];
|
||||||
|
outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
|
||||||
|
outputStream.write(headerAndData.array());
|
||||||
|
outputStream.flush();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.e(TAG, "failed closing output stream", e);
|
Slog.e(TAG, "failed writing to the persistent data block", e);
|
||||||
|
return -1;
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computeAndWriteDigestLocked()) {
|
||||||
|
return data.length;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,6 +337,9 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] read() {
|
public byte[] read() {
|
||||||
enforceUid(Binder.getCallingUid());
|
enforceUid(Binder.getCallingUid());
|
||||||
|
if (!enforceChecksumValidity()) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
DataInputStream inputStream;
|
DataInputStream inputStream;
|
||||||
try {
|
try {
|
||||||
@@ -256,30 +400,10 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
}
|
}
|
||||||
enforceOemUnlockPermission();
|
enforceOemUnlockPermission();
|
||||||
enforceIsOwner();
|
enforceIsOwner();
|
||||||
FileOutputStream outputStream;
|
|
||||||
try {
|
|
||||||
outputStream = new FileOutputStream(new File(mDataBlockFile));
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Slog.e(TAG, "parition not available", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
synchronized (mLock) {
|
||||||
FileChannel channel = outputStream.getChannel();
|
doSetOemUnlockEnabledLocked(enabled);
|
||||||
|
computeAndWriteDigestLocked();
|
||||||
channel.position(getBlockDeviceSize() - 1);
|
|
||||||
|
|
||||||
ByteBuffer data = ByteBuffer.allocate(1);
|
|
||||||
data.put(enabled ? (byte) 1 : (byte) 0);
|
|
||||||
data.flip();
|
|
||||||
|
|
||||||
synchronized (mOemLock) {
|
|
||||||
channel.write(data);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Slog.e(TAG, "unable to access persistent partition", e);
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeQuietly(outputStream);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,8 +419,8 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputStream.skip(getBlockDeviceSize() - 1);
|
synchronized (mLock) {
|
||||||
synchronized (mOemLock) {
|
inputStream.skip(getBlockDeviceSize() - 1);
|
||||||
return inputStream.readByte() != 0;
|
return inputStream.readByte() != 0;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -336,6 +460,5 @@ public class PersistentDataBlockService extends SystemService {
|
|||||||
long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
|
long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
|
||||||
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
|
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user